From f150e8aff1251707ed3ab20e6f6e7a25210d12e7 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Wed, 21 Sep 2016 14:49:08 -0400 Subject: [PATCH 01/33] delete everything i didn't like it --- ent/array.go | 17 --- ent/class.go | 54 ---------- ent/cutl.go | 20 ---- ent/decoders.go | 232 --------------------------------------- ent/dict.go | 221 ------------------------------------- ent/entity.go | 23 ---- ent/env.go | 19 ++++ ent/field.go | 109 ------------------- ent/float.go | 93 ---------------- ent/float_decoders.go | 84 --------------- ent/handle.go | 24 ----- ent/log.go | 5 + ent/namespace.go | 246 ------------------------------------------ ent/primitive.go | 106 ------------------ ent/qangle.go | 74 ------------- ent/selection.go | 33 ------ ent/slotted.go | 28 ----- ent/symbol.go | 17 --- ent/type.go | 99 ----------------- ent/type_spec.go | 120 --------------------- ent/vector.go | 7 -- entity.go | 18 ---- main.go | 4 +- 23 files changed, 26 insertions(+), 1627 deletions(-) delete mode 100644 ent/array.go delete mode 100644 ent/class.go delete mode 100644 ent/cutl.go delete mode 100644 ent/decoders.go delete mode 100644 ent/dict.go delete mode 100644 ent/entity.go create mode 100644 ent/env.go delete mode 100644 ent/field.go delete mode 100644 ent/float.go delete mode 100644 ent/float_decoders.go delete mode 100644 ent/handle.go delete mode 100644 ent/namespace.go delete mode 100644 ent/primitive.go delete mode 100644 ent/qangle.go delete mode 100644 ent/slotted.go delete mode 100644 ent/symbol.go delete mode 100644 ent/type.go delete mode 100644 ent/type_spec.go delete mode 100644 ent/vector.go delete mode 100644 entity.go diff --git a/ent/array.go b/ent/array.go deleted file mode 100644 index 7788c8a..0000000 --- a/ent/array.go +++ /dev/null @@ -1,17 +0,0 @@ -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 deleted file mode 100644 index bd09da3..0000000 --- a/ent/class.go +++ /dev/null @@ -1,54 +0,0 @@ -package ent - -import ( - "fmt" - "github.com/jordanorelli/hyperstone/dota" -) - -// Class represents a set of constraints around an Entity. -type Class struct { - name Symbol - Version int - Fields []*Field - - // all other entities for this class use this instance as a prototype - baseline *Entity - - // maps field names back to their indexes. Entities use this to access - // their own fields by name instead of by slot. - fieldNames map[string]int -} - -func (c *Class) Name() string { return c.name.String() } -func (c *Class) Slotted() bool { return true } -func (c *Class) Id() classId { return classId{name: c.name, version: c.Version} } - -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 { - return fmt.Sprintf("{%s %d}", c.Name, c.Version) -} - -// A class is identified by the union of its name and version. -type classId struct { - name Symbol - version int -} - -func (c *Class) fromProto(v *dota.ProtoFlattenedSerializerT, fields []Field) { - c.Fields = make([]*Field, len(v.GetFieldsIndex())) - for i, fi := range v.GetFieldsIndex() { - c.Fields[i] = &fields[fi] - } -} diff --git a/ent/cutl.go b/ent/cutl.go deleted file mode 100644 index 3b4b968..0000000 --- a/ent/cutl.go +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100644 index c47c503..0000000 --- a/ent/decoders.go +++ /dev/null @@ -1,232 +0,0 @@ -package ent - -import ( - // "strings" - - "github.com/jordanorelli/hyperstone/bit" -) - -// a decoder decodes an entity value off of a bit reader -type decoder func(bit.Reader) interface{} - -// creates a new field decoder for the field f. -func newFieldDecoder(n *Namespace, f *Field) decoder { - Debug.Printf("new decoder: type: %s name: %s sendNode: %s\n\tbits: %d low: %v high: %v\n\tflags: %d serializer: %v serializerVersion: %v\n\tclass: %v encoder: %v", f._type, f.name, f.sendNode, f.bits, f.low, f.high, f.flags, f.serializer, f.serializerVersion, f.class, f.encoder) - - typeName := f._type.String() - - switch typeName { - case "bool": - return decodeBool - case "uint8", "uint16", "uint32", "uint64": - return decodeVarInt64 - case "Color": - return decodeColor - case "int8", "int16", "int32", "int64": - return decodeZigZag - case "CNetworkedQuantizedFloat", "float32": - return floatDecoder(f) - case "Vector": - return vectorDecoder(f) - case "QAngle": - return qangleDecoder(f) - case "CGameSceneNodeHandle": - // ehhh maybe no? - return decodeVarInt32 - case "CUtlStringToken": - return symbolDecoder(n) - case "CUtlSymbolLarge", "char": - return decodeString - } - - // the field is itself an entity contained within the outer entity. - if f.class != nil { - return entityDecoder(f.class) - } - - // for compound types such as handles, vectors (in the c++ std::vector - // sense), and arrays - switch f.typeSpec.kind { - case t_element: - 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", f.typeSpec.name) - return nil - } - case t_object: - return func(br bit.Reader) interface{} { - Debug.Printf("unable to decode object of type: %v", f.typeSpec.name) - return decodeVarInt32(br) - } - case t_array: - return arrayDecoder(n, f) - case t_template: - return templateDecoder(f) - case t_pointer: - return decodeBool - } - - panic("fart") -} - -func decodeBool(br bit.Reader) interface{} { return bit.ReadBool(br) } -func decodeVarInt32(br bit.Reader) interface{} { return bit.ReadVarInt32(br) } -func decodeVarInt64(br bit.Reader) interface{} { return bit.ReadVarInt(br) } -func decodeZigZag(br bit.Reader) interface{} { return bit.ReadZigZag(br) } -func decodeString(br bit.Reader) interface{} { return bit.ReadString(br) } - -type color struct{ r, g, b, a uint8 } - -func decodeColor(br bit.Reader) interface{} { - u := bit.ReadVarInt(br) - return color{ - r: uint8(u & 0xff000000), - g: uint8(u & 0x00ff0000), - b: uint8(u & 0x0000ff00), - a: uint8(u & 0x000000ff), - } -} - -func entityDecoder(c *Class) decoder { - return func(br bit.Reader) interface{} { - bit.ReadBool(br) // what does this do - return c.New(-1, false) - } -} - -func vectorDecoder(f *Field) decoder { - if f.encoder != nil { - switch f.encoder.String() { - case "normal": - return decodeNormalVector - default: - return nil - } - } - - fn := floatDecoder(f) - if fn == nil { - return nil - } - return func(br bit.Reader) interface{} { - return vector{fn(br).(float32), fn(br).(float32), fn(br).(float32)} - } -} - -func decodeNormalVector(br bit.Reader) interface{} { - var v vector - x, y := bit.ReadBool(br), bit.ReadBool(br) - if x { - v.x = bit.ReadNormal(br) - } - if y { - v.y = bit.ReadNormal(br) - } - - // here comes a shitty hack! - bit.ReadBool(br) - // discard this flag, it's concerned with the sign of the z value, which - // we're skipping. - return v - - // ok, so. I have a suspicion that what we're interested in here is a - // surface normal, and that it's describing something about the geometry of - // the scene. I can't for the life of me see why in the hell we'd *ever* - // care about this in the context of parsing a replay, so I'm just turning - // off the z calculation. What follows is the original implementation as - // adapted from Manta and Clarity. The reason I care to skip this is that - // it involves a sqrt, which is a super slow operatation, and one worth - // dispensing with if you don't need the data that it produces. - // - // p := v.x*v.x + v.y*v.y - // if p < 1.0 { - // v.z = float32(math.Sqrt(float64(1.0 - p))) - // } - // if bit.ReadBool(br) { - // // we might wind up with the float representation of negative zero here, - // // but as far as I can tell, negative zero is fine because negative - // // zero is equivalent to positive zero. They'll print differently, but - // // I don't think we care about that. - // v.z = -v.z - // } - // return v -} - -// decodes a qangle, which is a representation of an euler angle. that is, it's -// a three-angle encoding of orientation. -// https://developer.valvesoftware.com/wiki/QAngle -// -// the x, y, and z in this case do not refer to positions along a set of -// cartesian axes, but degress of rotation in an Euler angle. -// -// (-45,10,0) means 45° up, 10° left and 0° roll. -func qangleDecoder(f *Field) decoder { - if f.encoder != nil && f.encoder.String() == "qangle_pitch_yaw" { - return nil - } - bits := f.bits - if bits == 32 { - return nil - } - if bits == 0 { - return func(br bit.Reader) interface{} { - var ( - v vector - x = bit.ReadBool(br) - y = bit.ReadBool(br) - z = bit.ReadBool(br) - ) - if x { - v.x = bit.ReadCoord(br) - } - if y { - v.y = bit.ReadCoord(br) - } - if z { - v.z = bit.ReadCoord(br) - } - return v - } - } - return func(br bit.Reader) interface{} { - return vector{ - x: bit.ReadAngle(br, bits), - y: bit.ReadAngle(br, bits), - z: bit.ReadAngle(br, bits), - } - } -} - -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) decoder { - switch f.typeSpec.template { - case "CHandle": - return decodeVarInt32 - case "CStrongHandle": - return decodeVarInt64 - case "CUtlVector": - 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 -} - -// so far a sanity check on the values I'm seeing out of this seem wrong. -func symbolDecoder(n *Namespace) decoder { - return func(br bit.Reader) interface{} { - u := bit.ReadVarInt32(br) - return n.Symbol(int(u)) - } -} diff --git a/ent/dict.go b/ent/dict.go deleted file mode 100644 index 4f69bdb..0000000 --- a/ent/dict.go +++ /dev/null @@ -1,221 +0,0 @@ -package ent - -import ( - "fmt" - "strconv" - - "github.com/golang/protobuf/proto" - - "github.com/jordanorelli/hyperstone/bit" - "github.com/jordanorelli/hyperstone/dota" - "github.com/jordanorelli/hyperstone/stbl" -) - -// https://developer.valvesoftware.com/wiki/Entity_limit -// -// There can be up to 4096 entities. This total is split into two groups of 2048: -// Non-networked entities, which exist only on the client or server (e.g. -// death ragdolls on client, logicals on server). -// Entities with associated edicts, which can cross the client/server divide. -// -// If the game tries to assign a 2049th edict it will exit with an error -// message, but if it tries to create a 2049th non-networked entity it will -// merely refuse and print a warning to the console. The logic behind this -// may be that an entity spawned dynamically (i.e. not present in the map) -// but not assigned an edict probably isn't too important. -const e_limit = 2048 - -// Dict corresponds to the edict_t in Valve's documentation for the Source -// engine. See here: https://developer.valvesoftware.com/wiki/Edict_t -// -// From the Valve docs: -// edict_t ("entity dictionary") is an interface struct that allows entities -// to cross the client/server divide: with one attached, an entity has the -// same index at both ends. The edict also manages the state of the entity's -// DataTable and provides a common representation across all DLLs. It cannot -// be used on the client. -type Dict struct { - *Namespace - entities []*Entity - hidx map[int]*Entity // handle index - br *bit.BufReader - sr *selectionReader - - // a reference to our string table of entity baseline data. For whatever - // reason, the first set of baselines sometimes come in before the classes - // are defined. - base *stbl.Table -} - -func NewDict(sd *stbl.Dict) *Dict { - d := &Dict{ - Namespace: new(Namespace), - entities: make([]*Entity, e_limit), - br: new(bit.BufReader), - sr: new(selectionReader), - base: sd.TableForName("instancebaseline"), - } - sd.WatchTable("instancebaseline", d.updateBaselines) - return d -} - -// creates an entity with the provided id. the entity's contents data are read -// off of the Dict's internal bit stream br -func (d *Dict) createEntity(id int) error { - classId := int(d.readClassId(d.br)) - if len(d.Namespace.classes) == 0 { - return fmt.Errorf("unable to create entity %d: namespace has no classes", id) - } - serial := int(d.br.ReadBits(17)) - classV := int(bit.ReadVarInt(d.br)) - className := d.classIds[classId] - class := d.Class(className, classV) - 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, 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) - if err := fillSlots(e, class.Name(), d.sr, d.br); err != nil { - return fmt.Errorf("failed to create entity %d (%s): %v", id, className, err) - } - return nil -} - -func (d *Dict) getEntity(id int) *Entity { - if id < 0 || id >= e_limit { - Debug.Printf("edict refused getEntity request for invalid id %d", id) - return nil - } - return d.entities[id] -} - -func (d *Dict) updateEntity(id int) error { - Debug.Printf("update entity id: %d\n", id) - e := d.getEntity(id) - if e == nil { - return fmt.Errorf("update entity %d refused: no such entity", id) - } - if err := fillSlots(e, e.Class.String(), d.sr, d.br); err != nil { - return fmt.Errorf("failed to update entity %d (%s): %v", id, e.Class.Name(), err) - } - return nil -} - -func (d *Dict) deleteEntity(id int) error { - Debug.Printf("delete entity id: %d\n", id) - if id < 0 || id >= e_limit { - // we're probably reading corrupt shit if we get here - return fmt.Errorf("delete entity %d refused: id is out of bounds", id) - } - if d.entities[id] == nil { - // this one's probably ok to let fly, but it's probably a sign that - // something is wrong elsewhere. - Debug.Printf("delete entity %d refused: no such entity", id) - return nil - } - d.entities[id] = nil - return nil -} - -func (d *Dict) leaveEntity(id int) error { - Debug.Printf("leave entity id: %d\n", id) - // what the shit does this do? - return nil -} - -func (d *Dict) Handle(m proto.Message) { - switch v := m.(type) { - case *dota.CDemoSendTables: - d.mergeSendTables(v) - d.syncBaselines() - - case *dota.CDemoClassInfo: - d.mergeClassInfo(v) - d.syncBaselines() - - case *dota.CSVCMsg_PacketEntities: - if err := d.mergeEntities(v); err != nil { - Info.Fatalf("merge entities error: %v", err) - } - } -} - -func (d *Dict) mergeEntities(m *dota.CSVCMsg_PacketEntities) error { - data := m.GetEntityData() - - Debug.Printf("packet header MaxEntries: %d UpdatedEntries: %v IsDelta: %t UpdateBaseline: %t Baseline: %d DeltaFrom: %d PendingFullFrame: %t ActiveSpawngroupHandle: %d", m.GetMaxEntries(), m.GetUpdatedEntries(), m.GetIsDelta(), m.GetUpdateBaseline(), m.GetBaseline(), m.GetDeltaFrom(), m.GetPendingFullFrame(), m.GetActiveSpawngroupHandle()) - - d.br.SetSource(data) - id := -1 - for i := 0; i < int(m.GetUpdatedEntries()); i++ { - id++ - // there may be a jump indicator, indicating how many id positions - // to skip. - id += int(bit.ReadUBitVar(d.br)) - - // next two bits encode one of four entity mutate operations - var fn func(int) error - switch d.br.ReadBits(2) { - case 0: - fn = d.updateEntity - case 1: - fn = d.leaveEntity - case 2: - fn = d.createEntity - case 3: - fn = d.deleteEntity - } - - if err := fn(id); err != nil { - return fmt.Errorf("error on entity %d: %v", id, err) - } - } - return nil -} - -func (d *Dict) updateBaselines(t *stbl.Table) { - d.base = t - d.syncBaselines() -} - -func (d *Dict) syncBaselines() error { - if !d.hasClassinfo() { - Debug.Printf("syncBaselines skip: no classInfo yet") - return nil - } - Debug.Printf("syncBaselines start") - if d.base == nil { - return fmt.Errorf("syncBaselines failed: reference to baseline string table is nil") - } - - for _, e := range d.base.Entries() { - id, err := strconv.Atoi(e.Key) - if err != nil { - Debug.Printf("syncBaselines skipping entry with key %s: key failed to parse to integer: %v", e.Key, err) - continue - } - - c := d.ClassByNetId(id) - if c == nil { - Debug.Printf("syncBaselines skipping entry with key %s: no such class", e.Key) - continue - } - - if c.baseline == nil { - c.baseline = c.New(-1, true) - } - - if e.Value == nil || len(e.Value) == 0 { - Debug.Printf("syncBaselines skipping entry with key %s: value is empty", e.Key) - continue - } - - d.br.SetSource(e.Value) - Debug.Printf("syncBaselines has new baseline for class %v", c) - if err := fillSlots(c.baseline, c.Name(), d.sr, d.br); err != nil { - return fmt.Errorf("syncBaselines failed to fill a baseline: %v", err) - } - } - return nil -} diff --git a/ent/entity.go b/ent/entity.go deleted file mode 100644 index cc19d92..0000000 --- a/ent/entity.go +++ /dev/null @@ -1,23 +0,0 @@ -package ent - -type Entity struct { - *Class - 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{} { - 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/env.go b/ent/env.go new file mode 100644 index 0000000..314e60a --- /dev/null +++ b/ent/env.go @@ -0,0 +1,19 @@ +package ent + +import ( + "github.com/golang/protobuf/proto" + + "github.com/jordanorelli/hyperstone/bit" +) + +type Env struct { + source bit.BufReader +} + +func (e *Env) Handle(m proto.Message) error { + return nil +} + +func (e *Env) setSource(buf []byte) { + e.source.SetSource(buf) +} diff --git a/ent/field.go b/ent/field.go deleted file mode 100644 index 3d4aad5..0000000 --- a/ent/field.go +++ /dev/null @@ -1,109 +0,0 @@ -package ent - -import ( - "bytes" - "fmt" - - "github.com/jordanorelli/hyperstone/dota" -) - -/* -type Field struct { - Name string - Type -} - -*/ - -type Field struct { - _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? - low float32 // lower limit of field values - high float32 // upper limit of field values - flags int // used by float decoder - serializer *Symbol // the field is an entity with this class - serializerVersion *int32 - 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 - initializer func() interface{} - isTemplate bool // whether or not the field is a template type - templateType string - elemType string -} - -func (f Field) String() string { - var buf bytes.Buffer - fmt.Fprintf(&buf, "{type: %s name: %s send: %s", f._type, f.name, f.sendNode) - if f.bits > 0 { - fmt.Fprintf(&buf, " bits: %d", f.bits) - } - if f.flags > 0 { - fmt.Fprintf(&buf, " flags: %d", f.flags) - } - fmt.Fprintf(&buf, " low: %f", f.low) - fmt.Fprintf(&buf, " high: %f", f.high) - if f.serializer != nil { - fmt.Fprintf(&buf, " serializer: %s", *f.serializer) - } - if f.serializerVersion != nil { - fmt.Fprintf(&buf, " serializer_v: %d", *f.serializerVersion) - } - if f.encoder != nil { - fmt.Fprintf(&buf, " encoder: %s", *f.encoder) - } - fmt.Fprint(&buf, "}") - return buf.String() -} - -func (f *Field) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, t *SymbolTable) { - f._type = t.Symbol(int(flat.GetVarTypeSym())) - f.name = t.Symbol(int(flat.GetVarNameSym())) - f.bits = uint(flat.GetBitCount()) - f.flags = int(flat.GetEncodeFlags()) - f.low = flat.GetLowValue() - f.high = flat.GetHighValue() - f.initializer = nilInitializer - - if flat.FieldSerializerNameSym == nil { - f.serializer = nil - } else { - f.serializer = new(Symbol) - *f.serializer = t.Symbol(int(flat.GetFieldSerializerNameSym())) - } - - f.serializerVersion = flat.FieldSerializerVersion - // panic if we don't have a send node cause that shit is corrupt yo - 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.go b/ent/float.go deleted file mode 100644 index b1efe3c..0000000 --- a/ent/float.go +++ /dev/null @@ -1,93 +0,0 @@ -package ent - -import ( - "fmt" - "math" - - "github.com/jordanorelli/hyperstone/bit" - "github.com/jordanorelli/hyperstone/dota" -) - -const ( - f_min = 1 << iota - f_max - f_center -) - -func parseFloatType(n *Namespace, flat *dota.ProtoFlattenedSerializerFieldT) (Type, error) { - if flat.VarEncoderSym != nil { - encoder := n.Symbol(int(flat.GetVarEncoderSym())).String() - switch encoder { - case "coord": - return nil, fmt.Errorf("coord encoder isn't dont yet") - default: - return nil, fmt.Errorf("unknown float encoder: %s", encoder) - } - } - - if flat.BitCount != nil { - bits := int(flat.GetBitCount()) - switch { - case bits < 0: - return nil, fmt.Errorf("invalid bit count on float field: %d", bits) - case bits < 32: - return quantizedFloat(n, flat) - case bits == 0, bits == 32: - // these seem meaningless, which is suspicious. - default: - return nil, fmt.Errorf("bit count is too high on float field: %d", bits) - } - } - - if flat.LowValue != nil || flat.HighValue != nil { - return nil, fmt.Errorf("float32 with a low or high value isn't supported") - } - - type_name := n.Symbol(int(flat.GetVarTypeSym())).String() - return &Primitive{name: type_name, read: readFloat32}, nil -} - -func quantizedFloat(n *Namespace, flat *dota.ProtoFlattenedSerializerFieldT) (Type, error) { - if flat.LowValue == nil && flat.HighValue == nil { - return nil, fmt.Errorf("quantizedFloat has no boundaries") - } - - bits := uint(flat.GetBitCount()) - low, high := flat.GetLowValue(), flat.GetHighValue() - flags := int(flat.GetEncodeFlags()) - - flags = flags & 7 // dunno how to handle -8 lol - steps := uint(1< 0: - special = &low - case flags&f_max > 0: - special = &high - case flags&f_center > 0: - middle := (high + low) * 0.5 - special = &middle - } - - read := func(br bit.Reader, d *Dict) (interface{}, error) { - if special != nil && bit.ReadBool(br) { - return *special, nil - } - u := br.ReadBits(bits) - return low + float32(u)*step_width, br.Err() - } - - type_name := n.Symbol(int(flat.GetVarTypeSym())).String() - return &Primitive{name: type_name, read: read}, nil -} - -// reads an IEEE 754 binary float value off of the stream -func readFloat32(br bit.Reader, d *Dict) (interface{}, error) { - return math.Float32frombits(uint32(br.ReadBits(32))), nil -} diff --git a/ent/float_decoders.go b/ent/float_decoders.go deleted file mode 100644 index 4dc61e9..0000000 --- a/ent/float_decoders.go +++ /dev/null @@ -1,84 +0,0 @@ -package ent - -import ( - "math" - - "github.com/jordanorelli/hyperstone/bit" -) - -func floatDecoder(f *Field) decoder { - if f.encoder != nil && f.encoder.String() == "coord" { - return nil - } - - if f.bits <= 0 || f.bits >= 32 { - return ieeeFloat32Decoder - } - - // a quantized field value must have some range specified, otherwise the - // quantization makes no sense. - if f.low == 0 && f.high == 0 { - panic("quantization rules make no sense") - } - - bits := f.bits - low := f.low - high := f.high - - flags := f.flags - - // there's a flag that's -8 and i don't know what to do with it. I'm just - // gonna mask away everything except the three least significant bits and - // pray for the best. - flags = flags & 7 - - // number of input steps - steps := int(1< 0: - special = new(float32) - *special = low - case flags&f_max > 0: - special = new(float32) - *special = high - case flags&f_center > 0: - special = new(float32) - middle := (high + low) * 0.5 - // if we're within a step of zero just return zero. - if middle > 0 && middle-step_width < 0 || middle < 0 && middle+step_width > 0 { - middle = 0 - } - *special = middle - } - - return func(br bit.Reader) interface{} { - if special != nil && bit.ReadBool(br) { - Debug.Printf("decode float type: %s low: %f high: %f bits: %d steps: %d span: %f flags: %d special: %v", f._type.String(), low, high, bits, steps, span, flags, *special) - return *special - } - u := br.ReadBits(bits) - out := low + float32(u)*inv_steps*span - Debug.Printf("decode float type: %s low: %f high: %f bits: %d bitVal: %d steps: %d span: %f flags: %d output: %v", f._type.String(), low, high, bits, u, steps, span, flags, out) - return out - } -} - -// reads an IEEE 754 binary float value off of the stream -func ieeeFloat32Decoder(br bit.Reader) interface{} { - return math.Float32frombits(uint32(br.ReadBits(32))) -} diff --git a/ent/handle.go b/ent/handle.go deleted file mode 100644 index cb58e21..0000000 --- a/ent/handle.go +++ /dev/null @@ -1,24 +0,0 @@ -package ent - -import ( - "fmt" - "github.com/jordanorelli/hyperstone/bit" -) - -type Handle struct{ name string } - -func (h *Handle) Name() string { return h.name } -func (h *Handle) New(...interface{}) interface{} { return nil } -func (h *Handle) Slotted() bool { return false } - -func (h *Handle) Read(br bit.Reader, d *Dict) (interface{}, error) { - id := int(bit.ReadVarInt(br)) - e, ok := d.hidx[id] - if !ok { - if br.Err() != nil { - return nil, br.Err() - } - return nil, fmt.Errorf("no entity found with handle %d", id) - } - return e, br.Err() -} diff --git a/ent/log.go b/ent/log.go index 106312e..4898e4a 100644 --- a/ent/log.go +++ b/ent/log.go @@ -1,6 +1,7 @@ package ent import ( + "fmt" "io/ioutil" "log" "os" @@ -10,3 +11,7 @@ var ( Debug = log.New(ioutil.Discard, "DEBUG ent: ", 0) Info = log.New(os.Stdout, "INFO end: ", 0) ) + +func wrap(err error, t string, args ...interface{}) error { + return fmt.Errorf(t+": %v", append(args, err)...) +} diff --git a/ent/namespace.go b/ent/namespace.go deleted file mode 100644 index cc44c64..0000000 --- a/ent/namespace.go +++ /dev/null @@ -1,246 +0,0 @@ -package ent - -import ( - "fmt" - "math" - - "github.com/golang/protobuf/proto" - - "github.com/jordanorelli/hyperstone/bit" - "github.com/jordanorelli/hyperstone/dota" -) - -// Namespace registers the names of known classess, associating their ids to -// their network types. -type Namespace struct { - SymbolTable - idBits int - - // maps ClassInfo ids to class names - classIds map[int]string - - // maps classes to their {name, version} id pairs - classes map[classId]*Class - - // maps a class name to every version of the class - classesByName map[string]map[int]*Class -} - -// Merges in the ClassInfo data found in the replay protobufs -func (n *Namespace) mergeClassInfo(ci *dota.CDemoClassInfo) { - if n.classIds == nil { - n.classIds = make(map[int]string, len(ci.GetClasses())) - } - for _, class := range ci.GetClasses() { - n.classIds[int(class.GetClassId())] = class.GetNetworkName() - } - n.idBits = int(math.Floor(math.Log2(float64(len(n.classIds))))) + 1 -} - -func (n *Namespace) hasClassinfo() bool { - return n.classIds != nil && len(n.classIds) > 0 -} - -// merges the send table data found in the replay protobufs. The send table -// data contains a specification for an entity type system. -func (n *Namespace) mergeSendTables(st *dota.CDemoSendTables) error { - // sendtables only has one field, a binary data field. - Debug.Printf("merge send tables") - data := st.GetData() - br := bit.NewBytesReader(data) - - // body is length-prefixed - size := int(bit.ReadVarInt(br)) - - buf := make([]byte, size) - br.Read(buf) - - flat := dota.CSVCMsg_FlattenedSerializer{} - if err := proto.Unmarshal(buf, &flat); err != nil { - return fmt.Errorf("unable to merge send tables: %v", err) - } - - n.SymbolTable = SymbolTable(flat.GetSymbols()) - - // the full set of fields that may appear on the classes is read first. - // each class will have a list of fields. - fields := make([]Field, len(flat.GetFields())) - for i, f := range flat.GetFields() { - fields[i].fromProto(f, &n.SymbolTable) - } - - n.classes = make(map[classId]*Class, len(flat.GetSerializers())) - n.classesByName = make(map[string]map[int]*Class, len(flat.GetSerializers())) - - // each serializer in the source data generates a class. - for _, c := range flat.GetSerializers() { - name := n.Symbol(int(c.GetSerializerNameSym())) - version := int(c.GetSerializerVersion()) - - Debug.Printf("new class: %s %v", name, version) - class := Class{name: name, Version: version} - class.fromProto(c, fields) - - id := classId{name: name, version: version} - n.classes[id] = &class - - if n.classesByName[name.String()] == nil { - n.classesByName[name.String()] = map[int]*Class{version: &class} - } else { - n.classesByName[name.String()][version] = &class - } - } - - for i := range fields { - f := &fields[i] - if f.serializer != nil { - if f.serializerVersion != nil { - f.class = n.classesByName[f.serializer.String()][int(*f.serializerVersion)] - } else { - f.class = n.NewestClass(f.serializer.String()) - } - } - - // 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. - f.decoder = newFieldDecoder(n, f) - } - - for _, ff := range flat.GetFields() { - t, err := parseType(n, ff) - if err != nil { - Debug.Printf(" parseType error: %v", err) - } else { - Debug.Printf(" parseType type: %v", t) - } - } - - return br.Err() -} - -/* -func (n *Namespace) newMergeSendTables(st *dota.CDemoSendTables) error { - // sendtables only has one field, a binary data field. It's not immediately - // clear why this message exists at all when it could simply contain - // CSVMsg_FlattenedSerializer. - data := st.GetData() - br := bit.NewBytesReader(data) - // body is length-prefixed - size := int(bit.ReadVarInt(br)) - buf := make([]byte, size) - br.Read(buf) - flat := dota.CSVCMsg_FlattenedSerializer{} - if err := proto.Unmarshal(buf, &flat); err != nil { - return fmt.Errorf("unable to merge send tables: %v", err) - } - return n.mergeSerializers(&flat) -} - -func (n *Namespace) mergeSerializers(flat *dota.CSVCMsg_FlattenedSerializer) error { - // most of the names and associated strings are interned in a symbol table. - // We'll need these first since they're referenced throughought the class - // and field definitions. - n.SymbolTable = SymbolTable(flat.GetSymbols()) - - n.classes = make(map[classId]*Class, len(flat.GetSerializers())) - n.classesByName = make(map[string]map[int]*Class, len(flat.GetSerializers())) - - // some fields refer to classes, but classes are collections of fields. - // Their references are potentially cyclical. We start by creating empty - // classes so that fields may point to them. - for _, s := range flat.GetSerializers() { - name_sym := n.Symbol(int(s.GetSerializerNameSym())) - ver := int(s.GetSerializerVersion()) - class := Class{name: name_sym, Version: ver} - n.classes[class.Id()] = &class - if n.classesByName[class.Name()] == nil { - n.classesByName[class.Name()] = make(map[int]*Class) - } - n.classesByName[class.Name()][ver] = &class - } - - // type ProtoFlattenedSerializerFieldT struct { - // VarTypeSym *int32 - // VarNameSym *int32 - // VarEncoderSym *int32 - // FieldSerializerNameSym *int32 - // FieldSerializerVersion *int32 - // BitCount *int32 - // LowValue *float32 - // HighValue *float32 - // EncodeFlags *int32 - // SendNodeSym *int32 - // } - - // next we parse all of the fields along with their type definitions - fields := make(map[int]Field, len(flat.GetFields())) - for id, flatField := range flat.GetFields() { - t := parseType(n, flatField) - fields[id].Name = n.Symbol(int(flatField.GetVarNameSym())).String() - } - os.Exit(1) - return nil -} -*/ - -func (n *Namespace) readClassId(r bit.Reader) int { - return int(r.ReadBits(uint(n.idBits))) -} - -func (n *Namespace) Class(name string, version int) *Class { - return n.classesByName[name][version] -} - -// retrieves the newest version of a class, as referenced by name. -func (n *Namespace) NewestClass(name string) *Class { - versions, newest := n.classesByName[name], -1 - for v, _ := range versions { - if v > newest { - newest = v - } - } - if newest == -1 { - Info.Fatalf("class %s has no known versions in its version map", name) - } - return versions[newest] -} - -func (n *Namespace) ClassByNetId(id int) *Class { - name, ok := n.classIds[id] - if !ok { - Info.Fatalf("can't find class name for net id %d", id) - } - return n.NewestClass(name) -} - -func (n *Namespace) HasClass(name string) bool { - _, ok := n.classesByName[name] - return ok -} diff --git a/ent/primitive.go b/ent/primitive.go deleted file mode 100644 index ac87d02..0000000 --- a/ent/primitive.go +++ /dev/null @@ -1,106 +0,0 @@ -package ent - -import ( - "github.com/jordanorelli/hyperstone/bit" -) - -var primitives map[string]Primitive - -// a Primitive type in the ent object system. -type Primitive struct { - name string - read decodeFn - alloc func() interface{} -} - -func (p *Primitive) New(args ...interface{}) interface{} { - if p.alloc != nil { - return p.alloc() - } - return nil -} -func (p *Primitive) Name() string { return p.name } -func (p *Primitive) Slotted() bool { return false } - -func (p *Primitive) Read(br bit.Reader, d *Dict) (interface{}, error) { - return p.read(br, d) -} - -func init() { - types := []Primitive{ - {name: "bool", read: readBool}, - {name: "uint8", read: readUint8}, - {name: "uint16", read: readUint16}, - {name: "uint32", read: readUint32}, - {name: "uint64", read: readUint64}, - {name: "int8", read: readInt8}, - {name: "int16", read: readInt16}, - {name: "int32", read: readInt32}, - {name: "int64", read: readInt64}, - {name: "Color", read: readColor}, - {name: "HSequence", read: readIntMinusOne}, - } - primitives = make(map[string]Primitive, len(types)) - for _, t := range types { - primitives[t.name] = t - } -} - -func readBool(br bit.Reader, d *Dict) (interface{}, error) { - return bit.ReadBool(br), br.Err() -} - -// ------------------------------------------------------------------------------ -// uints -// ------------------------------------------------------------------------------ - -func readUint8(br bit.Reader, d *Dict) (interface{}, error) { - return uint8(br.ReadBits(8)), br.Err() -} - -func readUint16(br bit.Reader, d *Dict) (interface{}, error) { - return uint16(bit.ReadVarInt(br)), br.Err() -} - -func readUint32(br bit.Reader, d *Dict) (interface{}, error) { - return uint32(bit.ReadVarInt(br)), br.Err() -} - -func readUint64(br bit.Reader, d *Dict) (interface{}, error) { - return bit.ReadVarInt(br), br.Err() -} - -// ------------------------------------------------------------------------------ -// ints -// ------------------------------------------------------------------------------ - -func readInt8(br bit.Reader, d *Dict) (interface{}, error) { - return int8(bit.ReadZigZag(br)), br.Err() -} - -func readInt16(br bit.Reader, d *Dict) (interface{}, error) { - return int16(bit.ReadZigZag(br)), br.Err() -} - -func readInt32(br bit.Reader, d *Dict) (interface{}, error) { - return int32(bit.ReadZigZag(br)), br.Err() -} - -func readInt64(br bit.Reader, d *Dict) (interface{}, error) { - return bit.ReadZigZag(br), br.Err() -} - -func readColor(br bit.Reader, d *Dict) (interface{}, error) { - u := bit.ReadVarInt(br) - return color{ - r: uint8(u & 0xff000000), - g: uint8(u & 0x00ff0000), - b: uint8(u & 0x0000ff00), - a: uint8(u & 0x000000ff), - }, br.Err() -} - -// what in the good fuck is this -func readIntMinusOne(br bit.Reader, d *Dict) (interface{}, error) { - return bit.ReadVarInt(br) - 1, br.Err() -} diff --git a/ent/qangle.go b/ent/qangle.go deleted file mode 100644 index 9e34dcf..0000000 --- a/ent/qangle.go +++ /dev/null @@ -1,74 +0,0 @@ -package ent - -import ( - "fmt" - "math" - - "github.com/jordanorelli/hyperstone/bit" - "github.com/jordanorelli/hyperstone/dota" -) - -func parseQAngleType(n *Namespace, flat *dota.ProtoFlattenedSerializerFieldT) (Type, error) { - type_name := n.Symbol(int(flat.GetVarTypeSym())).String() - if flat.VarEncoderSym != nil { - encoder := n.Symbol(int(flat.GetVarEncoderSym())).String() - switch encoder { - case "qangle_pitch_yaw": - return nil, fmt.Errorf("that qangle pitch yaw thing isn't done yet") - default: - return nil, fmt.Errorf("unknown qangle encoder: %s", encoder) - } - } - if flat.BitCount == nil { - return nil, fmt.Errorf("dunno what to do when qangle type has no bitcount") - } - if flat.GetBitCount() < 0 { - return nil, fmt.Errorf("negative bit count wtf") - } - bits := uint(flat.GetBitCount()) - switch bits { - case 0: - return &Primitive{name: type_name, read: readQAngleCoords}, nil - case 32: - return &Primitive{name: type_name, read: readQAngleFloats}, nil - default: - return &Primitive{name: type_name, read: qangleReader(bits)}, nil - } -} - -func qangleReader(bits uint) decodeFn { - return func(br bit.Reader, d *Dict) (interface{}, error) { - return &vector{ - x: bit.ReadAngle(br, bits), - y: bit.ReadAngle(br, bits), - z: bit.ReadAngle(br, bits), - }, br.Err() - } -} - -func readQAngleCoords(br bit.Reader, d *Dict) (interface{}, error) { - var ( - v vector - x = bit.ReadBool(br) - y = bit.ReadBool(br) - z = bit.ReadBool(br) - ) - if x { - v.x = bit.ReadCoord(br) - } - if y { - v.y = bit.ReadCoord(br) - } - if z { - v.z = bit.ReadCoord(br) - } - return v, br.Err() -} - -func readQAngleFloats(br bit.Reader, d *Dict) (interface{}, error) { - return &vector{ - x: math.Float32frombits(uint32(br.ReadBits(32))), - y: math.Float32frombits(uint32(br.ReadBits(32))), - z: math.Float32frombits(uint32(br.ReadBits(32))), - }, br.Err() -} diff --git a/ent/selection.go b/ent/selection.go index 4a4a4ec..42f4ab7 100644 --- a/ent/selection.go +++ b/ent/selection.go @@ -19,39 +19,6 @@ type selection struct { func (s selection) String() string { return fmt.Sprint(s.path()) } 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] - if s.count-offset <= 0 { - panic("selection makes no sense") - } - switch s.count - offset { - case 1: - fn := dest.slotDecoder(slot) - if fn == nil { - switch dest.(type) { - case *Entity: - Debug.Printf("%s %s (%s)", s, fmt.Sprintf("%s.%s", displayPath, dest.slotName(slot)), dest.slotType(slot)) - return nil - default: - Info.Printf("slotted value %v has no decoder for slot %d", dest, slot) - return nil - } - } - old := dest.slotValue(slot) - 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) - return nil - default: - v := dest.slotValue(slot) - vs, ok := v.(slotted) - if !ok { - return fmt.Errorf("selection %s at offset %d (%d) refers to a slot (%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 s.fill(offset+1, fmt.Sprintf("%s.%s", displayPath, dest.slotName(slot)), vs, br) - } -} - // selectionReader reads a set of field selections off of the wire. the // selections are represented as arrays of slot positions to be traversed in // order to select an entity slot. diff --git a/ent/slotted.go b/ent/slotted.go deleted file mode 100644 index ecf53db..0000000 --- a/ent/slotted.go +++ /dev/null @@ -1,28 +0,0 @@ -package ent - -import ( - "fmt" - "github.com/jordanorelli/hyperstone/bit" -) - -type slotted interface { - slotName(int) string - slotValue(int) interface{} - slotType(int) string - slotDecoder(int) decoder - setSlotValue(int, interface{}) -} - -func fillSlots(dest slotted, displayPath string, sr *selectionReader, br bit.Reader) error { - selections, err := sr.readSelections(br, htree) - if err != nil { - return fmt.Errorf("error filling slots: %v", err) - } - - for _, s := range selections { - if err := s.fill(0, displayPath, dest, br); err != nil { - return fmt.Errorf("error filling slots: %v", err) - } - } - return nil -} diff --git a/ent/symbol.go b/ent/symbol.go deleted file mode 100644 index 726293c..0000000 --- a/ent/symbol.go +++ /dev/null @@ -1,17 +0,0 @@ -package ent - -// the internal representation of table data refers to all labels as -// interned strings (symbols). This array of string contains the mapping of -// symbol ids to symbol display representations. The sample replay I have -// at the time of writing this contains 2215 symbols in its symbol table. -// The dota replay format uses an ordered list of symbols. -type SymbolTable []string - -func (t *SymbolTable) Symbol(id int) Symbol { return Symbol{id: id, table: t} } - -type Symbol struct { - id int - table *SymbolTable -} - -func (s Symbol) String() string { return (*s.table)[s.id] } diff --git a/ent/type.go b/ent/type.go deleted file mode 100644 index ffc9dcd..0000000 --- a/ent/type.go +++ /dev/null @@ -1,99 +0,0 @@ -package ent - -import ( - "bytes" - "fmt" - "github.com/jordanorelli/hyperstone/bit" - "github.com/jordanorelli/hyperstone/dota" -) - -type decodeFn func(bit.Reader, *Dict) (interface{}, error) - -// a Type in the entity type system. Note that not every type is necessarily an -// entity type, since there are necessarily primitives, and above that, arrays -// and generics. -type Type interface { - // creates a new value of the given type. - New(...interface{}) interface{} - - // name is primarily of interest for debugging - Name() string - - // whether or not the produced values are expected to be slotted. - Slotted() bool - - // reads a value of this type off of the bit reader - Read(bit.Reader, *Dict) (interface{}, error) -} - -func parseType(n *Namespace, flat *dota.ProtoFlattenedSerializerFieldT) (Type, error) { - Debug.Printf("parseType: %s", prettyFlatField(n, flat)) - type_name := n.Symbol(int(flat.GetVarTypeSym())).String() - - if prim, ok := primitives[type_name]; ok { - Debug.Printf(" parseType: found primitive with name %s", type_name) - return &prim, nil - } - - if n.HasClass(type_name) { - Debug.Printf(" parseType: found class with name %s", type_name) - return nil, nil - } - - switch type_name { - case "float32", "CNetworkedQuantizedFloat": - Debug.Printf(" parseType: parsing as float type") - return parseFloatType(n, flat) - case "CGameSceneNodeHandle": - return &Handle{name: "CGameSceneNodeHandle"}, nil - case "QAngle": - return parseQAngleType(n, flat) - } - - Debug.Printf(" parseType: failed") - // type ProtoFlattenedSerializerFieldT struct { - // VarTypeSym *int32 - // VarNameSym *int32 - // VarEncoderSym *int32 - // FieldSerializerNameSym *int32 - // FieldSerializerVersion *int32 - // BitCount *int32 - // LowValue *float32 - // HighValue *float32 - // EncodeFlags *int32 - // SendNodeSym *int32 - // } - return nil, nil -} - -func prettyFlatField(n *Namespace, ff *dota.ProtoFlattenedSerializerFieldT) string { - var buf bytes.Buffer - fmt.Fprintf(&buf, "{type: %s", n.Symbol(int(ff.GetVarTypeSym()))) - fmt.Fprintf(&buf, " name: %s", n.Symbol(int(ff.GetVarNameSym()))) - if ff.BitCount != nil { - fmt.Fprintf(&buf, " bits: %d", ff.GetBitCount()) - } - if ff.LowValue != nil { - fmt.Fprintf(&buf, " low: %f", ff.GetLowValue()) - } - if ff.HighValue != nil { - fmt.Fprintf(&buf, " high: %f", ff.GetHighValue()) - } - if ff.EncodeFlags != nil { - fmt.Fprintf(&buf, " flags: %d", ff.GetEncodeFlags()) - } - if ff.FieldSerializerNameSym != nil { - fmt.Fprintf(&buf, " serializer: %s", n.Symbol(int(ff.GetFieldSerializerNameSym()))) - } - if ff.FieldSerializerVersion != nil { - fmt.Fprintf(&buf, " version: %d", ff.GetFieldSerializerVersion()) - } - if ff.SendNodeSym != nil { - fmt.Fprintf(&buf, " send: %s", n.Symbol(int(ff.GetSendNodeSym()))) - } - if ff.VarEncoderSym != nil { - fmt.Fprintf(&buf, " encoder: %s", n.Symbol(int(ff.GetVarEncoderSym()))) - } - fmt.Fprintf(&buf, "}") - return buf.String() -} diff --git a/ent/type_spec.go b/ent/type_spec.go deleted file mode 100644 index a099d2d..0000000 --- a/ent/type_spec.go +++ /dev/null @@ -1,120 +0,0 @@ -package ent - -import ( - "fmt" - "strconv" - "strings" -) - -const ( - t_element = iota // a known element class - 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 -var cIdents = map[string]int{"MAX_ABILITY_DRAFT_ABILITIES": 48} - -type typeSpec struct { - name string - kind int - size int - template string - member *typeSpec -} - -func (t typeSpec) String() string { - if t.member != nil { - return fmt.Sprintf("{%v %v %v %v %v}", t.name, t.kind, t.size, t.template, *t.member) - } - return fmt.Sprintf("{%v %v %v %v %v}", t.name, t.kind, t.size, t.template, t.member) -} - -func parseTypeName(n *Namespace, s string) typeSpec { - s = strings.TrimSpace(s) - t := typeSpec{name: s} - - if n.HasClass(s) { - t.kind = t_element - 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) - t.kind = t_array - t.size = count - t.member = new(typeSpec) - *t.member = parseTypeName(n, memName) - return t - } - - if strings.ContainsRune(s, '<') { - t.kind = t_template - template, member := parseTemplateName(s) - t.template = template - t.member = new(typeSpec) - *t.member = parseTypeName(n, member) - return t - } - - t.kind = t_object - return t -} - -func parseArrayName(s string) (string, int) { - runes := []rune(s) - if runes[len(runes)-1] != ']' { - panic("invalid array type name: " + s) - } - for i := len(runes) - 2; i >= 0; i-- { - if runes[i] == '[' { - ns := strings.TrimSpace(string(runes[i+1 : len(runes)-1])) - n, err := strconv.Atoi(ns) - if err != nil { - n = cIdents[ns] - if n <= 0 { - panic("invalid array type name: " + err.Error()) - } - } - return strings.TrimSpace(string(runes[:i])), n - } - } - panic("invalid array type name: " + s) -} - -func parseTemplateName(s string) (string, string) { - if strings.ContainsRune(s, ',') { - panic("can't handle templates with multiple parameters") - } - - runes := []rune(s) - template := "" - depth := 0 - bracket_start := -1 - for i, r := range runes { - if r == '<' { - if depth == 0 { - bracket_start = i - template = strings.TrimSpace(string(runes[:i])) - } - depth++ - } - if r == '>' { - depth-- - if depth == 0 { - return template, strings.TrimSpace(string(runes[bracket_start+1 : i])) - } - } - } - panic("invalid template type definition: " + s) -} diff --git a/ent/vector.go b/ent/vector.go deleted file mode 100644 index 1f11c96..0000000 --- a/ent/vector.go +++ /dev/null @@ -1,7 +0,0 @@ -package ent - -type vector struct { - x float32 - y float32 - z float32 -} diff --git a/entity.go b/entity.go deleted file mode 100644 index 7cdb0e3..0000000 --- a/entity.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "fmt" -) - -type entity struct { - t entityType - size uint32 - body []byte -} - -func (e entity) String() string { - if len(e.body) < 30 { - return fmt.Sprintf("{entity type: %s size: %d data: %x}", e.t, e.size, e.body) - } - return fmt.Sprintf("{entity type: %s size: %d data: %x...}", e.t, e.size, e.body[:27]) -} diff --git a/main.go b/main.go index 5b32148..59f3379 100644 --- a/main.go +++ b/main.go @@ -143,10 +143,10 @@ func main() { case "entities": ent.Debug = log.New(os.Stdout, "", 0) sd := stbl.NewDict() - ed := ent.NewDict(sd) + env := new(ent.Env) handle = func(m proto.Message) { sd.Handle(m) - ed.Handle(m) + env.Handle(m) } default: bail(1, "no such action: %s", flag.Arg(0)) From 53793b695ccbbc12d3ab8d07bdb23fd9af0a799f Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Fri, 23 Sep 2016 18:10:03 -0400 Subject: [PATCH 02/33] outlining --- ent/class.go | 6 +++++ ent/env.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++- ent/field.go | 47 +++++++++++++++++++++++++++++++++++++ ent/misc.go | 27 ++++++++++++++++++++++ ent/symbol.go | 10 ++++++++ ent/type.go | 4 ++++ ent/value.go | 9 ++++++++ main.go | 4 +++- 8 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 ent/class.go create mode 100644 ent/field.go create mode 100644 ent/misc.go create mode 100644 ent/symbol.go create mode 100644 ent/type.go create mode 100644 ent/value.go diff --git a/ent/class.go b/ent/class.go new file mode 100644 index 0000000..27e2d30 --- /dev/null +++ b/ent/class.go @@ -0,0 +1,6 @@ +package ent + +type class struct { +} + +type classHistory map[int]*class diff --git a/ent/env.go b/ent/env.go index 314e60a..c8e607f 100644 --- a/ent/env.go +++ b/ent/env.go @@ -1,19 +1,81 @@ package ent import ( + "fmt" + "github.com/golang/protobuf/proto" "github.com/jordanorelli/hyperstone/bit" + "github.com/jordanorelli/hyperstone/dota" ) type Env struct { - source bit.BufReader + symbols symbolTable + source bit.BufReader + classes map[string]classHistory + fields []field } func (e *Env) Handle(m proto.Message) error { + switch v := m.(type) { + case *dota.CDemoSendTables: + return e.mergeSendTables(v) + } return nil } func (e *Env) setSource(buf []byte) { e.source.SetSource(buf) } + +func (e *Env) mergeSendTables(m *dota.CDemoSendTables) error { + Debug.Printf("merge send tables") + + flat, err := getSerializers(m) + if err != nil { + return wrap(err, "unable to get serializers in sendtables") + } + e.symbols = symbolTable(flat.GetSymbols()) + e.stubClasses(flat) + if err := e.parseFields(flat); err != nil { + return wrap(err, "unable to parse serializer fields") + } + if err := e.parseClasses(flat); err != nil { + return wrap(err, "unable to parse serializers") + } + return nil +} + +// stubs out the classes to be created later. we do this to create empty class +// structs that fields may point to. +func (e *Env) stubClasses(flat *dota.CSVCMsg_FlattenedSerializer) { + serializers := flat.GetSerializers() + if e.classes == nil { + e.classes = make(map[string]classHistory, len(serializers)) + } + for _, s := range serializers { + name := e.symbol(int(s.GetSerializerNameSym())) + v := int(s.GetSerializerVersion()) + if e.classes[name] == nil { + e.classes[name] = make(classHistory, 4) + } + e.classes[name][v] = new(class) + } +} + +func (e *Env) parseFields(flat *dota.CSVCMsg_FlattenedSerializer) error { + e.fields = make([]field, len(flat.GetFields())) + for i, ff := range flat.GetFields() { + f := &e.fields[i] + if err := f.fromProto(ff, e); err != nil { + return err + } + } + return nil +} + +func (e *Env) parseClasses(flat *dota.CSVCMsg_FlattenedSerializer) error { + return fmt.Errorf("nope, not yet") +} + +func (e *Env) symbol(id int) string { return e.symbols[id] } diff --git a/ent/field.go b/ent/field.go new file mode 100644 index 0000000..f8dfdc1 --- /dev/null +++ b/ent/field.go @@ -0,0 +1,47 @@ +package ent + +import ( + "bytes" + "fmt" + "github.com/jordanorelli/hyperstone/dota" +) + +type field struct { + name string + tÿpe +} + +func (f *field) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) error { + var_name := env.symbol(int(flat.GetVarNameSym())) + var_type := env.symbol(int(flat.GetVarTypeSym())) + + var pretty bytes.Buffer + fmt.Fprintf(&pretty, "{name: %s type: %s", var_name, var_type) + if flat.BitCount != nil { + fmt.Fprintf(&pretty, " bits: %d", flat.GetBitCount()) + } + if flat.LowValue != nil { + fmt.Fprintf(&pretty, " low: %f", flat.GetLowValue()) + } + if flat.HighValue != nil { + fmt.Fprintf(&pretty, " high: %f", flat.GetHighValue()) + } + if flat.EncodeFlags != nil { + fmt.Fprintf(&pretty, " flags: %d", flat.GetEncodeFlags()) + } + if flat.FieldSerializerNameSym != nil { + fmt.Fprintf(&pretty, " serializer: %s", env.symbol(int(flat.GetFieldSerializerNameSym()))) + } + if flat.FieldSerializerVersion != nil { + fmt.Fprintf(&pretty, " s_version: %d", flat.GetFieldSerializerVersion()) + } + if flat.SendNodeSym != nil { + fmt.Fprintf(&pretty, " send: %s", env.symbol(int(flat.GetSendNodeSym()))) + } + if flat.VarEncoderSym != nil { + fmt.Fprintf(&pretty, " var_enc: %s", env.symbol(int(flat.GetVarEncoderSym()))) + } + fmt.Fprint(&pretty, "}") + + return fmt.Errorf("unable to parse type: %s", pretty.String()) +} diff --git a/ent/misc.go b/ent/misc.go new file mode 100644 index 0000000..f79d0db --- /dev/null +++ b/ent/misc.go @@ -0,0 +1,27 @@ +package ent + +import ( + "github.com/golang/protobuf/proto" + + "github.com/jordanorelli/hyperstone/bit" + "github.com/jordanorelli/hyperstone/dota" +) + +func getSerializers(m *dota.CDemoSendTables) (*dota.CSVCMsg_FlattenedSerializer, error) { + data := m.GetData() + r := bit.NewBytesReader(data) + + size := int(bit.ReadVarInt(r)) + buf := make([]byte, size) + + r.Read(buf) + if r.Err() != nil { + return nil, wrap(r.Err(), "error reading serializers body") + } + + flat := dota.CSVCMsg_FlattenedSerializer{} + if err := proto.Unmarshal(buf, &flat); err != nil { + return nil, wrap(err, "error unmarshaling serializers body") + } + return &flat, nil +} diff --git a/ent/symbol.go b/ent/symbol.go new file mode 100644 index 0000000..0bb1fe3 --- /dev/null +++ b/ent/symbol.go @@ -0,0 +1,10 @@ +package ent + +type symbol struct { + id int + table *symbolTable +} + +func (s symbol) String() string { return (*s.table)[s.id] } + +type symbolTable []string diff --git a/ent/type.go b/ent/type.go new file mode 100644 index 0000000..31583d9 --- /dev/null +++ b/ent/type.go @@ -0,0 +1,4 @@ +package ent + +type tÿpe interface { +} diff --git a/ent/value.go b/ent/value.go new file mode 100644 index 0000000..453442b --- /dev/null +++ b/ent/value.go @@ -0,0 +1,9 @@ +package ent + +import ( + "github.com/jordanorelli/hyperstone/bit" +) + +type value interface { + read(bit.Reader) error +} diff --git a/main.go b/main.go index 59f3379..454bed5 100644 --- a/main.go +++ b/main.go @@ -146,7 +146,9 @@ func main() { env := new(ent.Env) handle = func(m proto.Message) { sd.Handle(m) - env.Handle(m) + if err := env.Handle(m); err != nil { + bail(1, "%v", err) + } } default: bail(1, "no such action: %s", flag.Arg(0)) From f1c4db00fb604e621cce5220f74087a55f6b172a Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Fri, 23 Sep 2016 18:36:04 -0400 Subject: [PATCH 03/33] uint16 plz6 plz6 plz6 plz6 plz6 plz --- ent/atoms.go | 16 ++++++++++++++++ ent/field.go | 33 +++++---------------------------- ent/misc.go | 37 +++++++++++++++++++++++++++++++++++++ ent/type.go | 4 +++- 4 files changed, 61 insertions(+), 29 deletions(-) create mode 100644 ent/atoms.go diff --git a/ent/atoms.go b/ent/atoms.go new file mode 100644 index 0000000..6e4be52 --- /dev/null +++ b/ent/atoms.go @@ -0,0 +1,16 @@ +package ent + +import ( + "github.com/jordanorelli/hyperstone/bit" +) + +var atom_types = map[string]tÿpe{ + "uint16": {"uint16", func(...interface{}) value { return new(uint16_v) }}, +} + +type uint16_v uint16 + +func (u *uint16_v) read(r bit.Reader) error { + *u = uint16_v(bit.ReadVarInt(r)) + return r.Err() +} diff --git a/ent/field.go b/ent/field.go index f8dfdc1..cc18357 100644 --- a/ent/field.go +++ b/ent/field.go @@ -1,7 +1,6 @@ package ent import ( - "bytes" "fmt" "github.com/jordanorelli/hyperstone/dota" ) @@ -15,33 +14,11 @@ func (f *field) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) e var_name := env.symbol(int(flat.GetVarNameSym())) var_type := env.symbol(int(flat.GetVarTypeSym())) - var pretty bytes.Buffer - fmt.Fprintf(&pretty, "{name: %s type: %s", var_name, var_type) - if flat.BitCount != nil { - fmt.Fprintf(&pretty, " bits: %d", flat.GetBitCount()) + if t, ok := atom_types[var_type]; ok { + f.name = var_name + f.tÿpe = t + return nil } - if flat.LowValue != nil { - fmt.Fprintf(&pretty, " low: %f", flat.GetLowValue()) - } - if flat.HighValue != nil { - fmt.Fprintf(&pretty, " high: %f", flat.GetHighValue()) - } - if flat.EncodeFlags != nil { - fmt.Fprintf(&pretty, " flags: %d", flat.GetEncodeFlags()) - } - if flat.FieldSerializerNameSym != nil { - fmt.Fprintf(&pretty, " serializer: %s", env.symbol(int(flat.GetFieldSerializerNameSym()))) - } - if flat.FieldSerializerVersion != nil { - fmt.Fprintf(&pretty, " s_version: %d", flat.GetFieldSerializerVersion()) - } - if flat.SendNodeSym != nil { - fmt.Fprintf(&pretty, " send: %s", env.symbol(int(flat.GetSendNodeSym()))) - } - if flat.VarEncoderSym != nil { - fmt.Fprintf(&pretty, " var_enc: %s", env.symbol(int(flat.GetVarEncoderSym()))) - } - fmt.Fprint(&pretty, "}") - return fmt.Errorf("unable to parse type: %s", pretty.String()) + return fmt.Errorf("unable to parse type: %s", prettyFlatField(flat, env)) } diff --git a/ent/misc.go b/ent/misc.go index f79d0db..8c08273 100644 --- a/ent/misc.go +++ b/ent/misc.go @@ -1,6 +1,9 @@ package ent import ( + "bytes" + "fmt" + "github.com/golang/protobuf/proto" "github.com/jordanorelli/hyperstone/bit" @@ -25,3 +28,37 @@ func getSerializers(m *dota.CDemoSendTables) (*dota.CSVCMsg_FlattenedSerializer, } return &flat, nil } + +func prettyFlatField(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) string { + var_name := env.symbol(int(flat.GetVarNameSym())) + var_type := env.symbol(int(flat.GetVarTypeSym())) + + var pretty bytes.Buffer + fmt.Fprintf(&pretty, "{name: %s type: %s", var_name, var_type) + if flat.BitCount != nil { + fmt.Fprintf(&pretty, " bits: %d", flat.GetBitCount()) + } + if flat.LowValue != nil { + fmt.Fprintf(&pretty, " low: %f", flat.GetLowValue()) + } + if flat.HighValue != nil { + fmt.Fprintf(&pretty, " high: %f", flat.GetHighValue()) + } + if flat.EncodeFlags != nil { + fmt.Fprintf(&pretty, " flags: %d", flat.GetEncodeFlags()) + } + if flat.FieldSerializerNameSym != nil { + fmt.Fprintf(&pretty, " serializer: %s", env.symbol(int(flat.GetFieldSerializerNameSym()))) + } + if flat.FieldSerializerVersion != nil { + fmt.Fprintf(&pretty, " s_version: %d", flat.GetFieldSerializerVersion()) + } + if flat.SendNodeSym != nil { + fmt.Fprintf(&pretty, " send: %s", env.symbol(int(flat.GetSendNodeSym()))) + } + if flat.VarEncoderSym != nil { + fmt.Fprintf(&pretty, " var_enc: %s", env.symbol(int(flat.GetVarEncoderSym()))) + } + fmt.Fprint(&pretty, "}") + return pretty.String() +} diff --git a/ent/type.go b/ent/type.go index 31583d9..6213c78 100644 --- a/ent/type.go +++ b/ent/type.go @@ -1,4 +1,6 @@ package ent -type tÿpe interface { +type tÿpe struct { + name string + alloc func(...interface{}) value } From 17dd5d399a07bb6da2f0f6047dcdd101c583413f Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 08:33:19 -0400 Subject: [PATCH 04/33] two types now --- ent/atoms.go | 18 ++++++++------ ent/field.go | 15 ++++++------ ent/float.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ent/type.go | 40 ++++++++++++++++++++++++++++--- ent/value.go | 8 +------ 5 files changed, 123 insertions(+), 25 deletions(-) create mode 100644 ent/float.go diff --git a/ent/atoms.go b/ent/atoms.go index 6e4be52..26b9576 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -2,15 +2,19 @@ package ent import ( "github.com/jordanorelli/hyperstone/bit" + "github.com/jordanorelli/hyperstone/dota" ) -var atom_types = map[string]tÿpe{ - "uint16": {"uint16", func(...interface{}) value { return new(uint16_v) }}, +var atom_types = map[string]typeFn{ + "uint16": func(r bit.Reader) (value, error) { + return uint16(bit.ReadVarInt(r)), r.Err() + }, } -type uint16_v uint16 - -func (u *uint16_v) read(r bit.Reader) error { - *u = uint16_v(bit.ReadVarInt(r)) - return r.Err() +func atomType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { + var_type := env.symbol(int(flat.GetVarTypeSym())) + if t, ok := atom_types[var_type]; ok { + return t + } + return nil } diff --git a/ent/field.go b/ent/field.go index cc18357..97d8657 100644 --- a/ent/field.go +++ b/ent/field.go @@ -11,14 +11,13 @@ type field struct { } func (f *field) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) error { - var_name := env.symbol(int(flat.GetVarNameSym())) - var_type := env.symbol(int(flat.GetVarTypeSym())) - - if t, ok := atom_types[var_type]; ok { - f.name = var_name - f.tÿpe = t - return nil + Debug.Printf("parse flat field: %s", prettyFlatField(flat, env)) + t := parseType(flat, env) + if t == nil { + return fmt.Errorf("unable to parse type: %s", prettyFlatField(flat, env)) } - return fmt.Errorf("unable to parse type: %s", prettyFlatField(flat, env)) + f.tÿpe = t + f.name = env.symbol(int(flat.GetVarNameSym())) + return nil } diff --git a/ent/float.go b/ent/float.go new file mode 100644 index 0000000..c7fd834 --- /dev/null +++ b/ent/float.go @@ -0,0 +1,67 @@ +package ent + +import ( + "fmt" + + "github.com/jordanorelli/hyperstone/bit" + "github.com/jordanorelli/hyperstone/dota" +) + +const ( + f_min = 1 << iota + f_max + f_center +) + +func qFloatType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { + if env.symbol(int(flat.GetVarTypeSym())) != "CNetworkedQuantizedFloat" { + return nil + } + if flat.GetBitCount() < 0 { + return typeError("quantized float has invalid negative bit count specifier") + } + if flat.GetHighValue()-flat.GetLowValue() < 0 { + return typeError("quantized float has invalid negative range") + } + t := qfloat_t{ + bits: uint(flat.GetBitCount()), + low: flat.GetLowValue(), + high: flat.GetHighValue(), + flags: int(flat.GetEncodeFlags()) & 0x7, + } + t.span = t.high - t.low + t.intervals = uint(1< 0 { + t.special = new(float32) + } + + switch { + case t.flags&f_min > 0: + *t.special = t.low + case t.flags&f_max > 0: + *t.special = t.high + case t.flags&f_center > 0: + *t.special = t.low + (t.high+t.low)*0.5 + } + return t +} + +type qfloat_t struct { + bits uint + low float32 + high float32 + flags int + span float32 // total range of values + intervals uint // number of intervals in the quantization range + interval float32 // width of one interval + special *float32 +} + +func (t qfloat_t) read(r bit.Reader) (value, error) { + if t.special != nil && bit.ReadBool(r) { + return *t.special, nil + } + return nil, fmt.Errorf("I'll get there") +} diff --git a/ent/type.go b/ent/type.go index 6213c78..6806b2c 100644 --- a/ent/type.go +++ b/ent/type.go @@ -1,6 +1,40 @@ package ent -type tÿpe struct { - name string - alloc func(...interface{}) value +import ( + "fmt" + + "github.com/jordanorelli/hyperstone/bit" + "github.com/jordanorelli/hyperstone/dota" +) + +type tÿpe interface { + read(bit.Reader) (value, error) +} + +type typeFn func(bit.Reader) (value, error) + +func (fn typeFn) read(r bit.Reader) (value, error) { return fn(r) } + +type typeParseFn func(*dota.ProtoFlattenedSerializerFieldT, *Env) tÿpe + +func parseType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { + coalesce := func(fns ...typeParseFn) tÿpe { + for _, fn := range fns { + if t := fn(flat, env); t != nil { + return t + } + } + return nil + } + return coalesce(atomType, qFloatType) +} + +// a type error is both an error and a type. It represents a type that we were +// unable to correctly parse. It can be interpreted as an error or as a type; +// when interpreted as a type, it errors every time it tries to read a value. +type typeError string + +func (e typeError) Error() string { return string(e) } +func (e typeError) read(r bit.Reader) (value, error) { + return nil, fmt.Errorf("type error: %s", string(e)) } diff --git a/ent/value.go b/ent/value.go index 453442b..2b252df 100644 --- a/ent/value.go +++ b/ent/value.go @@ -1,9 +1,3 @@ package ent -import ( - "github.com/jordanorelli/hyperstone/bit" -) - -type value interface { - read(bit.Reader) error -} +type value interface{} From 6d7e67b2bfb2dcd2ef5f0c0500b7c22f366d41e9 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 08:41:40 -0400 Subject: [PATCH 05/33] handle --- ent/handle.go | 23 +++++++++++++++++++++++ ent/type.go | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 ent/handle.go diff --git a/ent/handle.go b/ent/handle.go new file mode 100644 index 0000000..19cef53 --- /dev/null +++ b/ent/handle.go @@ -0,0 +1,23 @@ +package ent + +import ( + "github.com/jordanorelli/hyperstone/bit" + "github.com/jordanorelli/hyperstone/dota" +) + +func handleType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { + if env.symbol(int(flat.GetVarTypeSym())) != "CGameSceneNodeHandle" { + return nil + } + return handle_t{} +} + +// a handle represents a soft pointer to an entity. handles are represented by +// IDs and can cross the client-server divide. +type handle int + +type handle_t struct{} + +func (t handle_t) read(r bit.Reader) (value, error) { + return handle(bit.ReadVarInt(r)), nil +} diff --git a/ent/type.go b/ent/type.go index 6806b2c..d73f43c 100644 --- a/ent/type.go +++ b/ent/type.go @@ -26,7 +26,7 @@ func parseType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { } return nil } - return coalesce(atomType, qFloatType) + return coalesce(atomType, qFloatType, handleType) } // a type error is both an error and a type. It represents a type that we were From c0c9f22fbaf08471e70565f9df86c1132aa20e92 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 11:44:07 -0400 Subject: [PATCH 06/33] qangle and vector definitions --- ent/float.go | 28 ++++++++++++++++++++++++---- ent/handle.go | 17 +++++++---------- ent/qangle.go | 33 +++++++++++++++++++++++++++++++++ ent/type.go | 2 +- ent/vector.go | 3 +++ 5 files changed, 68 insertions(+), 15 deletions(-) create mode 100644 ent/qangle.go create mode 100644 ent/vector.go diff --git a/ent/float.go b/ent/float.go index c7fd834..b1e8a1e 100644 --- a/ent/float.go +++ b/ent/float.go @@ -1,7 +1,7 @@ package ent import ( - "fmt" + "math" "github.com/jordanorelli/hyperstone/bit" "github.com/jordanorelli/hyperstone/dota" @@ -13,10 +13,25 @@ const ( f_center ) -func qFloatType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { - if env.symbol(int(flat.GetVarTypeSym())) != "CNetworkedQuantizedFloat" { +func floatType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { + if env.symbol(int(flat.GetVarTypeSym())) == "CNetworkedQuantizedFloat" { + return qFloatType(flat, env) + } + if env.symbol(int(flat.GetVarEncoderSym())) == "coord" { + return nil + } + if env.symbol(int(flat.GetFieldSerializerNameSym())) == "simulationtime" { + return nil + } + switch flat.GetBitCount() { + case 0, 32: + return typeFn(float_t) + default: return nil } +} + +func qFloatType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { if flat.GetBitCount() < 0 { return typeError("quantized float has invalid negative bit count specifier") } @@ -63,5 +78,10 @@ func (t qfloat_t) read(r bit.Reader) (value, error) { if t.special != nil && bit.ReadBool(r) { return *t.special, nil } - return nil, fmt.Errorf("I'll get there") + return t.low + float32(r.ReadBits(t.bits))*t.interval, r.Err() +} + +func float_t(r bit.Reader) (value, error) { + // TODO: check uint32 overflow here? + return math.Float32frombits(uint32(r.ReadBits(32))), r.Err() } diff --git a/ent/handle.go b/ent/handle.go index 19cef53..eafd01e 100644 --- a/ent/handle.go +++ b/ent/handle.go @@ -5,19 +5,16 @@ import ( "github.com/jordanorelli/hyperstone/dota" ) -func handleType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { - if env.symbol(int(flat.GetVarTypeSym())) != "CGameSceneNodeHandle" { - return nil - } - return handle_t{} -} - // a handle represents a soft pointer to an entity. handles are represented by // IDs and can cross the client-server divide. type handle int -type handle_t struct{} +func handleType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { + if env.symbol(int(flat.GetVarTypeSym())) != "CGameSceneNodeHandle" { + return nil + } -func (t handle_t) read(r bit.Reader) (value, error) { - return handle(bit.ReadVarInt(r)), nil + return typeFn(func(r bit.Reader) (value, error) { + return handle(bit.ReadVarInt(r)), r.Err() + }) } diff --git a/ent/qangle.go b/ent/qangle.go new file mode 100644 index 0000000..d030ca1 --- /dev/null +++ b/ent/qangle.go @@ -0,0 +1,33 @@ +package ent + +import ( + "github.com/jordanorelli/hyperstone/bit" + "github.com/jordanorelli/hyperstone/dota" +) + +func qAngleType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { + if env.symbol(int(flat.GetVarTypeSym())) != "QAngle" { + return nil + } + switch flat.GetBitCount() { + case 0: + return typeFn(func(r bit.Reader) (value, error) { + x, y, z := bit.ReadBool(r), bit.ReadBool(r), bit.ReadBool(r) + var v vector + if x { + v.x = bit.ReadCoord(r) + } + if y { + v.y = bit.ReadCoord(r) + } + if z { + v.z = bit.ReadCoord(r) + } + return v, nil + }) + case 32: + return nil + default: + return nil + } +} diff --git a/ent/type.go b/ent/type.go index d73f43c..23b45b7 100644 --- a/ent/type.go +++ b/ent/type.go @@ -26,7 +26,7 @@ func parseType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { } return nil } - return coalesce(atomType, qFloatType, handleType) + return coalesce(atomType, floatType, handleType, qAngleType) } // a type error is both an error and a type. It represents a type that we were diff --git a/ent/vector.go b/ent/vector.go new file mode 100644 index 0000000..5dcd82e --- /dev/null +++ b/ent/vector.go @@ -0,0 +1,3 @@ +package ent + +type vector struct{ x, y, z float32 } From 36dbc9aaa96b8b6a84cb4fe4b00bd940e27195a6 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 12:06:41 -0400 Subject: [PATCH 07/33] typeError is nice --- ent/field.go | 5 ++++- ent/float.go | 18 ++++++++++-------- ent/type.go | 10 +++++++--- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/ent/field.go b/ent/field.go index 97d8657..dafc0be 100644 --- a/ent/field.go +++ b/ent/field.go @@ -14,7 +14,10 @@ func (f *field) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) e Debug.Printf("parse flat field: %s", prettyFlatField(flat, env)) t := parseType(flat, env) if t == nil { - return fmt.Errorf("unable to parse type: %s", prettyFlatField(flat, env)) + return fmt.Errorf("unable to parse type %s", prettyFlatField(flat, env)) + } + if err, ok := t.(error); ok { + return wrap(err, "unable to parse type %s", prettyFlatField(flat, env)) } f.tÿpe = t diff --git a/ent/float.go b/ent/float.go index b1e8a1e..59ae455 100644 --- a/ent/float.go +++ b/ent/float.go @@ -50,16 +50,18 @@ func qFloatType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { if t.flags > 0 { t.special = new(float32) + switch t.flags { + case f_min: + *t.special = t.low + case f_max: + *t.special = t.high + case f_center: + *t.special = t.low + (t.high+t.low)*0.5 + default: + return typeError("dunno how to handle qfloat flag value: %d", t.flags) + } } - switch { - case t.flags&f_min > 0: - *t.special = t.low - case t.flags&f_max > 0: - *t.special = t.high - case t.flags&f_center > 0: - *t.special = t.low + (t.high+t.low)*0.5 - } return t } diff --git a/ent/type.go b/ent/type.go index 23b45b7..81ddc4a 100644 --- a/ent/type.go +++ b/ent/type.go @@ -32,9 +32,13 @@ func parseType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { // a type error is both an error and a type. It represents a type that we were // unable to correctly parse. It can be interpreted as an error or as a type; // when interpreted as a type, it errors every time it tries to read a value. -type typeError string +func typeError(t string, args ...interface{}) tÿpe { + return error_t(fmt.Sprintf(t, args...)) +} + +type error_t string -func (e typeError) Error() string { return string(e) } -func (e typeError) read(r bit.Reader) (value, error) { +func (e error_t) Error() string { return string(e) } +func (e error_t) read(r bit.Reader) (value, error) { return nil, fmt.Errorf("type error: %s", string(e)) } From 1dff55ca67d245ded2adc8c31b93a443c12fe272 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 12:15:06 -0400 Subject: [PATCH 08/33] logging --- ent/atoms.go | 1 + ent/field.go | 1 + ent/float.go | 10 ++++++++-- ent/handle.go | 1 + ent/qangle.go | 1 + ent/type.go | 1 + 6 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ent/atoms.go b/ent/atoms.go index 26b9576..1d45f56 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -14,6 +14,7 @@ var atom_types = map[string]typeFn{ func atomType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { var_type := env.symbol(int(flat.GetVarTypeSym())) if t, ok := atom_types[var_type]; ok { + Debug.Printf(" atom type") return t } return nil diff --git a/ent/field.go b/ent/field.go index dafc0be..c9a7604 100644 --- a/ent/field.go +++ b/ent/field.go @@ -20,6 +20,7 @@ func (f *field) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) e return wrap(err, "unable to parse type %s", prettyFlatField(flat, env)) } + Debug.Printf(" type: %v", t) f.tÿpe = t f.name = env.symbol(int(flat.GetVarNameSym())) return nil diff --git a/ent/float.go b/ent/float.go index 59ae455..9a52b7a 100644 --- a/ent/float.go +++ b/ent/float.go @@ -14,8 +14,12 @@ const ( ) func floatType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { - if env.symbol(int(flat.GetVarTypeSym())) == "CNetworkedQuantizedFloat" { + switch env.symbol(int(flat.GetVarTypeSym())) { + case "CNetworkedQuantizedFloat": return qFloatType(flat, env) + case "float32": + default: + return nil } if env.symbol(int(flat.GetVarEncoderSym())) == "coord" { return nil @@ -25,9 +29,10 @@ func floatType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { } switch flat.GetBitCount() { case 0, 32: + Debug.Printf(" std float type") return typeFn(float_t) default: - return nil + return qFloatType(flat, env) } } @@ -62,6 +67,7 @@ func qFloatType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { } } + Debug.Printf(" qfloat type") return t } diff --git a/ent/handle.go b/ent/handle.go index eafd01e..4cfdb56 100644 --- a/ent/handle.go +++ b/ent/handle.go @@ -14,6 +14,7 @@ func handleType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { return nil } + Debug.Printf(" handle type") return typeFn(func(r bit.Reader) (value, error) { return handle(bit.ReadVarInt(r)), r.Err() }) diff --git a/ent/qangle.go b/ent/qangle.go index d030ca1..a28fd4a 100644 --- a/ent/qangle.go +++ b/ent/qangle.go @@ -11,6 +11,7 @@ func qAngleType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { } switch flat.GetBitCount() { case 0: + Debug.Printf(" qangle type") return typeFn(func(r bit.Reader) (value, error) { x, y, z := bit.ReadBool(r), bit.ReadBool(r), bit.ReadBool(r) var v vector diff --git a/ent/type.go b/ent/type.go index 81ddc4a..1e06217 100644 --- a/ent/type.go +++ b/ent/type.go @@ -33,6 +33,7 @@ func parseType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { // unable to correctly parse. It can be interpreted as an error or as a type; // when interpreted as a type, it errors every time it tries to read a value. func typeError(t string, args ...interface{}) tÿpe { + Debug.Printf(" type error: %s", fmt.Sprintf(t, args...)) return error_t(fmt.Sprintf(t, args...)) } From 92ff4ea5f79e14e1cdcef0fa48b5208899059f9a Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 12:23:14 -0400 Subject: [PATCH 09/33] hsequence what in the world --- ent/hseq.go | 17 +++++++++++++++++ ent/type.go | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 ent/hseq.go diff --git a/ent/hseq.go b/ent/hseq.go new file mode 100644 index 0000000..5fec275 --- /dev/null +++ b/ent/hseq.go @@ -0,0 +1,17 @@ +package ent + +import ( + "github.com/jordanorelli/hyperstone/bit" + "github.com/jordanorelli/hyperstone/dota" +) + +func hSeqType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { + if env.symbol(int(flat.GetVarTypeSym())) != "HSequence" { + return nil + } + + Debug.Printf(" hsequence type") + return typeFn(func(r bit.Reader) (value, error) { + return bit.ReadVarInt(r) - 1, r.Err() + }) +} diff --git a/ent/type.go b/ent/type.go index 1e06217..c352b2b 100644 --- a/ent/type.go +++ b/ent/type.go @@ -26,7 +26,7 @@ func parseType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { } return nil } - return coalesce(atomType, floatType, handleType, qAngleType) + return coalesce(atomType, floatType, handleType, qAngleType, hSeqType) } // a type error is both an error and a type. It represents a type that we were From 8025667a58b22545555300f241ba369e3f4d20f5 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 12:39:06 -0400 Subject: [PATCH 10/33] ints --- ent/atoms.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ent/atoms.go b/ent/atoms.go index 1d45f56..c530a99 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -7,8 +7,12 @@ import ( var atom_types = map[string]typeFn{ "uint16": func(r bit.Reader) (value, error) { + // TODO: bounds check here return uint16(bit.ReadVarInt(r)), r.Err() }, + "int32": func(r bit.Reader) (value, error) { + return int32(bit.ReadZigZag32(r)), r.Err() + }, } func atomType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { From 78b07284ca9fb551b413538b701cafa89b71cb2e Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 21:21:57 -0400 Subject: [PATCH 11/33] typeSpec type ahhhhhh this feels good. --- ent/atoms.go | 6 ++---- ent/field.go | 2 +- ent/float.go | 34 +++++++++++++--------------------- ent/handle.go | 5 ++--- ent/hseq.go | 5 ++--- ent/qangle.go | 7 +++---- ent/type.go | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 68 insertions(+), 39 deletions(-) diff --git a/ent/atoms.go b/ent/atoms.go index c530a99..34417e9 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -2,7 +2,6 @@ package ent import ( "github.com/jordanorelli/hyperstone/bit" - "github.com/jordanorelli/hyperstone/dota" ) var atom_types = map[string]typeFn{ @@ -15,9 +14,8 @@ var atom_types = map[string]typeFn{ }, } -func atomType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { - var_type := env.symbol(int(flat.GetVarTypeSym())) - if t, ok := atom_types[var_type]; ok { +func atomType(spec *typeSpec, env *Env) tÿpe { + if t, ok := atom_types[spec.typeName]; ok { Debug.Printf(" atom type") return t } diff --git a/ent/field.go b/ent/field.go index c9a7604..b2b7e8c 100644 --- a/ent/field.go +++ b/ent/field.go @@ -12,7 +12,7 @@ type field struct { func (f *field) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) error { Debug.Printf("parse flat field: %s", prettyFlatField(flat, env)) - t := parseType(flat, env) + t := parseFieldType(flat, env) if t == nil { return fmt.Errorf("unable to parse type %s", prettyFlatField(flat, env)) } diff --git a/ent/float.go b/ent/float.go index 9a52b7a..1e4e579 100644 --- a/ent/float.go +++ b/ent/float.go @@ -4,7 +4,6 @@ import ( "math" "github.com/jordanorelli/hyperstone/bit" - "github.com/jordanorelli/hyperstone/dota" ) const ( @@ -13,42 +12,38 @@ const ( f_center ) -func floatType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { - switch env.symbol(int(flat.GetVarTypeSym())) { +func floatType(spec *typeSpec, env *Env) tÿpe { + switch spec.typeName { case "CNetworkedQuantizedFloat": - return qFloatType(flat, env) + return qFloatType(spec, env) case "float32": default: return nil } - if env.symbol(int(flat.GetVarEncoderSym())) == "coord" { + if spec.encoder == "coord" { return nil } - if env.symbol(int(flat.GetFieldSerializerNameSym())) == "simulationtime" { + if spec.serializer == "simulationtime" { return nil } - switch flat.GetBitCount() { + switch spec.bits { case 0, 32: Debug.Printf(" std float type") return typeFn(float_t) default: - return qFloatType(flat, env) + return qFloatType(spec, env) } } -func qFloatType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { - if flat.GetBitCount() < 0 { +func qFloatType(spec *typeSpec, env *Env) tÿpe { + if spec.bits < 0 { return typeError("quantized float has invalid negative bit count specifier") } - if flat.GetHighValue()-flat.GetLowValue() < 0 { + if spec.high-spec.low < 0 { return typeError("quantized float has invalid negative range") } - t := qfloat_t{ - bits: uint(flat.GetBitCount()), - low: flat.GetLowValue(), - high: flat.GetHighValue(), - flags: int(flat.GetEncodeFlags()) & 0x7, - } + + t := qfloat_t{typeSpec: *spec} t.span = t.high - t.low t.intervals = uint(1< Date: Sat, 24 Sep 2016 22:27:55 -0400 Subject: [PATCH 12/33] stronghandle --- ent/generic.go | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ ent/type.go | 2 +- 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 ent/generic.go diff --git a/ent/generic.go b/ent/generic.go new file mode 100644 index 0000000..15c66ee --- /dev/null +++ b/ent/generic.go @@ -0,0 +1,69 @@ +package ent + +import ( + "fmt" + "strings" + + "github.com/jordanorelli/hyperstone/bit" +) + +func genericType(spec *typeSpec, env *Env) tÿpe { + if !strings.Contains(spec.typeName, "<") { + return nil + } + + parts, err := genericName(spec.typeName) + if err != nil { + return typeError("bad generic name: %v", err) + } + + // genericName, elemName := parts[0], parts[1] + // elemType := func() tÿpe { + // var elemSpec typeSpec + // elemSpec = *spec + // elemSpec.name = elemName + // return parseTypeSpec(&elemSpec, env) + // } + + switch parts[0] { + case "CStrongHandle": + return typeFn(func(r bit.Reader) (value, error) { + return handle(bit.ReadVarInt(r)), r.Err() + }) + default: + return typeError("unknown generic name: %v", genericName) + } +} + +func genericName(name string) ([2]string, error) { + var out [2]string + runes := []rune(strings.TrimSpace(name)) + b_start := 0 + depth := 0 + for i, r := range runes { + if r == '<' { + if depth == 0 { + out[0] = strings.TrimSpace(string(runes[0:i])) + } + depth++ + b_start = i + } + if r == '>' { + depth-- + if depth == 0 { + if i == len(runes)-1 { + out[1] = strings.TrimSpace(string(runes[b_start+1 : i])) + } else { + return out, fmt.Errorf("extra runes in generic type name") + } + } + } + } + if out[0] == "" { + return out, fmt.Errorf("empty generic container name") + } + if out[1] == "" { + return out, fmt.Errorf("empty generic element name") + } + return out, nil +} diff --git a/ent/type.go b/ent/type.go index 06b13be..62c6b23 100644 --- a/ent/type.go +++ b/ent/type.go @@ -32,7 +32,7 @@ func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { } return nil } - return coalesce(atomType, floatType, handleType, qAngleType, hSeqType) + return coalesce(atomType, floatType, handleType, qAngleType, hSeqType, genericType) } // a type error is both an error and a type. It represents a type that we were From 7349613e5677cd61d7a0d5108447644022dac920 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 22:29:50 -0400 Subject: [PATCH 13/33] some atoms --- ent/atoms.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ent/atoms.go b/ent/atoms.go index 34417e9..7eda21a 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -9,8 +9,15 @@ var atom_types = map[string]typeFn{ // TODO: bounds check here return uint16(bit.ReadVarInt(r)), r.Err() }, + "uint64": func(r bit.Reader) (value, error) { + return bit.ReadVarInt(r), r.Err() + }, + "int8": func(r bit.Reader) (value, error) { + // TODO: bounds check here + return int8(bit.ReadZigZag32(r)), r.Err() + }, "int32": func(r bit.Reader) (value, error) { - return int32(bit.ReadZigZag32(r)), r.Err() + return bit.ReadZigZag32(r), r.Err() }, } From 66fe4d7290b1d3fb65d540785f37983f7795dedd Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 22:34:08 -0400 Subject: [PATCH 14/33] more atoms --- ent/atoms.go | 7 +++++++ ent/string.go | 14 ++++++++++++++ ent/type.go | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 ent/string.go diff --git a/ent/atoms.go b/ent/atoms.go index 7eda21a..e4c38d6 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -5,6 +5,13 @@ import ( ) var atom_types = map[string]typeFn{ + "bool": func(r bit.Reader) (value, error) { + return bit.ReadBool(r), r.Err() + }, + "uint8": func(r bit.Reader) (value, error) { + // TODO: bounds check here + return uint8(bit.ReadVarInt(r)), r.Err() + }, "uint16": func(r bit.Reader) (value, error) { // TODO: bounds check here return uint16(bit.ReadVarInt(r)), r.Err() diff --git a/ent/string.go b/ent/string.go new file mode 100644 index 0000000..63aca57 --- /dev/null +++ b/ent/string.go @@ -0,0 +1,14 @@ +package ent + +import ( + "github.com/jordanorelli/hyperstone/bit" +) + +func stringType(spec *typeSpec, env *Env) tÿpe { + if spec.typeName != "CUtlStringToken" { + return nil + } + return typeFn(func(r bit.Reader) (value, error) { + return bit.ReadVarInt(r), r.Err() + }) +} diff --git a/ent/type.go b/ent/type.go index 62c6b23..e78a6bc 100644 --- a/ent/type.go +++ b/ent/type.go @@ -32,7 +32,7 @@ func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { } return nil } - return coalesce(atomType, floatType, handleType, qAngleType, hSeqType, genericType) + return coalesce(atomType, floatType, handleType, qAngleType, hSeqType, genericType, stringType) } // a type error is both an error and a type. It represents a type that we were From 357c43fd5445fbafe291554e8da6e904d33dae3a Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 22:38:09 -0400 Subject: [PATCH 15/33] that string token should be an atom --- ent/atoms.go | 3 +++ ent/string.go | 14 -------------- ent/type.go | 2 +- 3 files changed, 4 insertions(+), 15 deletions(-) delete mode 100644 ent/string.go diff --git a/ent/atoms.go b/ent/atoms.go index e4c38d6..9fa2d8f 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -26,6 +26,9 @@ var atom_types = map[string]typeFn{ "int32": func(r bit.Reader) (value, error) { return bit.ReadZigZag32(r), r.Err() }, + "CUtlStringToken": func(r bit.Reader) (value, error) { + return bit.ReadVarInt(r), r.Err() + }, } func atomType(spec *typeSpec, env *Env) tÿpe { diff --git a/ent/string.go b/ent/string.go deleted file mode 100644 index 63aca57..0000000 --- a/ent/string.go +++ /dev/null @@ -1,14 +0,0 @@ -package ent - -import ( - "github.com/jordanorelli/hyperstone/bit" -) - -func stringType(spec *typeSpec, env *Env) tÿpe { - if spec.typeName != "CUtlStringToken" { - return nil - } - return typeFn(func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r), r.Err() - }) -} diff --git a/ent/type.go b/ent/type.go index e78a6bc..62c6b23 100644 --- a/ent/type.go +++ b/ent/type.go @@ -32,7 +32,7 @@ func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { } return nil } - return coalesce(atomType, floatType, handleType, qAngleType, hSeqType, genericType, stringType) + return coalesce(atomType, floatType, handleType, qAngleType, hSeqType, genericType) } // a type error is both an error and a type. It represents a type that we were From 557d34dfd1b6c8ae91e3bc7d5a4c9cf2103cf1d8 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 22:56:38 -0400 Subject: [PATCH 16/33] array --- ent/array.go | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ent/type.go | 2 +- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 ent/array.go diff --git a/ent/array.go b/ent/array.go new file mode 100644 index 0000000..2a4a692 --- /dev/null +++ b/ent/array.go @@ -0,0 +1,63 @@ +package ent + +import ( + "strconv" + "strings" + + "github.com/jordanorelli/hyperstone/bit" +) + +var constants = map[string]int{ + "MAX_ABILITY_DRAFT_ABILITIES": 48, +} + +func arrayType(spec *typeSpec, env *Env) tÿpe { + if !strings.Contains(spec.typeName, "[") { + return nil + } + elemName, count := parseArrayName(spec.typeName) + elemSpec := *spec + elemSpec.typeName = elemName + elemType := parseTypeSpec(&elemSpec, env) + return array_t{elemType, count} +} + +func parseArrayName(s string) (string, int) { + runes := []rune(s) + if runes[len(runes)-1] != ']' { + panic("invalid array type name: " + s) + } + for i := len(runes) - 2; i >= 0; i-- { + if runes[i] == '[' { + ns := strings.TrimSpace(string(runes[i+1 : len(runes)-1])) + n, err := strconv.Atoi(ns) + if err != nil { + n = constants[ns] + if n <= 0 { + panic("invalid array type name: " + err.Error()) + } + } + return strings.TrimSpace(string(runes[:i])), n + } + } + panic("invalid array type name: " + s) +} + +type array_t struct { + elem tÿpe + count int +} + +func (t array_t) read(r bit.Reader) (value, error) { + var err error + v := make(array, t.count) + for i := range v { + v[i], err = t.elem.read(r) + if err != nil { + return nil, wrap(err, "array read error at index %d", i) + } + } + return v, r.Err() +} + +type array []value diff --git a/ent/type.go b/ent/type.go index 62c6b23..898b831 100644 --- a/ent/type.go +++ b/ent/type.go @@ -32,7 +32,7 @@ func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { } return nil } - return coalesce(atomType, floatType, handleType, qAngleType, hSeqType, genericType) + return coalesce(arrayType, atomType, floatType, handleType, qAngleType, hSeqType, genericType) } // a type error is both an error and a type. It represents a type that we were From fd457383241d4c27f16d4f22832ebcd0a57171de Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 23:00:34 -0400 Subject: [PATCH 17/33] color --- ent/atoms.go | 11 +++++++++++ ent/generic.go | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ent/atoms.go b/ent/atoms.go index 9fa2d8f..6436886 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -29,6 +29,15 @@ var atom_types = map[string]typeFn{ "CUtlStringToken": func(r bit.Reader) (value, error) { return bit.ReadVarInt(r), r.Err() }, + "Color": func(r bit.Reader) (value, error) { + u := bit.ReadVarInt(r) + return color{ + r: uint8(u >> 6 & 0xff), + g: uint8(u >> 4 & 0xff), + b: uint8(u >> 2 & 0xff), + a: uint8(u >> 0 & 0xff), + }, r.Err() + }, } func atomType(spec *typeSpec, env *Env) tÿpe { @@ -38,3 +47,5 @@ func atomType(spec *typeSpec, env *Env) tÿpe { } return nil } + +type color struct{ r, g, b, a uint8 } diff --git a/ent/generic.go b/ent/generic.go index 15c66ee..8ff8897 100644 --- a/ent/generic.go +++ b/ent/generic.go @@ -26,12 +26,12 @@ func genericType(spec *typeSpec, env *Env) tÿpe { // } switch parts[0] { - case "CStrongHandle": + case "CHandle", "CStrongHandle": return typeFn(func(r bit.Reader) (value, error) { return handle(bit.ReadVarInt(r)), r.Err() }) default: - return typeError("unknown generic name: %v", genericName) + return typeError("unknown generic name: %v", parts[0]) } } From c372f14e3384a0f2751949270f778f3a6dadbfae Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 24 Sep 2016 23:16:23 -0400 Subject: [PATCH 18/33] classes --- ent/class.go | 25 +++++++++++++++++++++++++ ent/env.go | 2 +- ent/type.go | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/ent/class.go b/ent/class.go index 27e2d30..41ada50 100644 --- a/ent/class.go +++ b/ent/class.go @@ -1,6 +1,31 @@ package ent +import ( + "fmt" + "github.com/jordanorelli/hyperstone/bit" +) + type class struct { + name string + version int +} + +func (c *class) read(r bit.Reader) (value, error) { + return nil, fmt.Errorf("fart") } type classHistory map[int]*class + +func classType(spec *typeSpec, env *Env) tÿpe { + if spec.serializer != "" { + h := env.classes[spec.serializer] + if h != nil { + class := h[spec.serializerV] + if class != nil { + return class + } + return typeError("class %s exists for spec serializer but can't find version %d", spec.serializer, spec.serializerV) + } + } + return nil +} diff --git a/ent/env.go b/ent/env.go index c8e607f..9fa6e9e 100644 --- a/ent/env.go +++ b/ent/env.go @@ -59,7 +59,7 @@ func (e *Env) stubClasses(flat *dota.CSVCMsg_FlattenedSerializer) { if e.classes[name] == nil { e.classes[name] = make(classHistory, 4) } - e.classes[name][v] = new(class) + e.classes[name][v] = &class{name: name, version: v} } } diff --git a/ent/type.go b/ent/type.go index 898b831..fe91389 100644 --- a/ent/type.go +++ b/ent/type.go @@ -32,7 +32,7 @@ func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { } return nil } - return coalesce(arrayType, atomType, floatType, handleType, qAngleType, hSeqType, genericType) + return coalesce(arrayType, atomType, floatType, handleType, qAngleType, hSeqType, genericType, classType) } // a type error is both an error and a type. It represents a type that we were From ee09904289c5a8b70f710e236f84310ae2ad0b21 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sun, 25 Sep 2016 00:25:45 -0400 Subject: [PATCH 19/33] this seems suspicious --- ent/atoms.go | 3 +++ ent/float.go | 1 + ent/type.go | 3 ++- ent/vector.go | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/ent/atoms.go b/ent/atoms.go index 6436886..af3e921 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -16,6 +16,9 @@ var atom_types = map[string]typeFn{ // TODO: bounds check here return uint16(bit.ReadVarInt(r)), r.Err() }, + "uint32": func(r bit.Reader) (value, error) { + return bit.ReadVarInt32(r), r.Err() + }, "uint64": func(r bit.Reader) (value, error) { return bit.ReadVarInt(r), r.Err() }, diff --git a/ent/float.go b/ent/float.go index 1e4e579..530a4d7 100644 --- a/ent/float.go +++ b/ent/float.go @@ -17,6 +17,7 @@ func floatType(spec *typeSpec, env *Env) tÿpe { case "CNetworkedQuantizedFloat": return qFloatType(spec, env) case "float32": + case "Vector": default: return nil } diff --git a/ent/type.go b/ent/type.go index fe91389..58c5386 100644 --- a/ent/type.go +++ b/ent/type.go @@ -32,7 +32,8 @@ func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { } return nil } - return coalesce(arrayType, atomType, floatType, handleType, qAngleType, hSeqType, genericType, classType) + return coalesce(arrayType, atomType, floatType, handleType, qAngleType, + hSeqType, genericType, vectorType, classType) } // a type error is both an error and a type. It represents a type that we were diff --git a/ent/vector.go b/ent/vector.go index 5dcd82e..2bdebca 100644 --- a/ent/vector.go +++ b/ent/vector.go @@ -1,3 +1,35 @@ package ent +import ( + "github.com/jordanorelli/hyperstone/bit" +) + type vector struct{ x, y, z float32 } + +func vectorType(spec *typeSpec, env *Env) tÿpe { + if spec.encoder != "" { + return nil + } + return vector_t{elem: floatType(spec, env)} +} + +type vector_t struct { + elem tÿpe +} + +func (t vector_t) read(r bit.Reader) (value, error) { + var err error + var v interface{} + read := func(f *float32) { + if err != nil { + return + } + v, err = t.elem.read(r) + *f = v.(float32) + } + var out vector + read(&out.x) + read(&out.y) + read(&out.z) + return out, err +} From 3b11093a32fa012ee176f77186f80a315ff7f445 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sun, 25 Sep 2016 00:30:33 -0400 Subject: [PATCH 20/33] coord --- ent/float.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ent/float.go b/ent/float.go index 530a4d7..5601ba6 100644 --- a/ent/float.go +++ b/ent/float.go @@ -22,7 +22,10 @@ func floatType(spec *typeSpec, env *Env) tÿpe { return nil } if spec.encoder == "coord" { - return nil + Debug.Printf(" coord float type") + return typeFn(func(r bit.Reader) (value, error) { + return bit.ReadCoord(r), r.Err() + }) } if spec.serializer == "simulationtime" { return nil @@ -30,7 +33,10 @@ func floatType(spec *typeSpec, env *Env) tÿpe { switch spec.bits { case 0, 32: Debug.Printf(" std float type") - return typeFn(float_t) + return typeFn(func(r bit.Reader) (value, error) { + // TODO: check uint32 overflow here? + return math.Float32frombits(uint32(r.ReadBits(32))), r.Err() + }) default: return qFloatType(spec, env) } @@ -81,8 +87,3 @@ func (t qfloat_t) read(r bit.Reader) (value, error) { } return t.low + float32(r.ReadBits(t.bits))*t.interval, r.Err() } - -func float_t(r bit.Reader) (value, error) { - // TODO: check uint32 overflow here? - return math.Float32frombits(uint32(r.ReadBits(32))), r.Err() -} From cf787781e9590f467d0389639915548a3f9f55d8 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sun, 25 Sep 2016 00:45:51 -0400 Subject: [PATCH 21/33] likely broken cutlvector --- ent/generic.go | 25 +++++++++++++++++-------- ent/type.go | 1 + 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/ent/generic.go b/ent/generic.go index 8ff8897..a37a212 100644 --- a/ent/generic.go +++ b/ent/generic.go @@ -17,19 +17,18 @@ func genericType(spec *typeSpec, env *Env) tÿpe { return typeError("bad generic name: %v", err) } - // genericName, elemName := parts[0], parts[1] - // elemType := func() tÿpe { - // var elemSpec typeSpec - // elemSpec = *spec - // elemSpec.name = elemName - // return parseTypeSpec(&elemSpec, env) - // } + genericName, elemName := parts[0], parts[1] + elemSpec := *spec + elemSpec.typeName = elemName + elem := parseTypeSpec(&elemSpec, env) - switch parts[0] { + switch genericName { case "CHandle", "CStrongHandle": return typeFn(func(r bit.Reader) (value, error) { return handle(bit.ReadVarInt(r)), r.Err() }) + case "CUtlVector": + return cutl_vector_t{elem} default: return typeError("unknown generic name: %v", parts[0]) } @@ -65,5 +64,15 @@ func genericName(name string) ([2]string, error) { if out[1] == "" { return out, fmt.Errorf("empty generic element name") } + Debug.Printf(" generic name in: %s out: %v", name, out) return out, nil } + +type cutl_vector_t struct { + elem tÿpe +} + +func (t cutl_vector_t) read(r bit.Reader) (value, error) { + count := bit.ReadVarInt32(r) + return make(array, count), r.Err() +} diff --git a/ent/type.go b/ent/type.go index 58c5386..8803c0d 100644 --- a/ent/type.go +++ b/ent/type.go @@ -24,6 +24,7 @@ func parseFieldType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe { } func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { + Debug.Printf(" parse spec: %v", spec) coalesce := func(fns ...typeParseFn) tÿpe { for _, fn := range fns { if t := fn(spec, env); t != nil { From df813020339b6587dd63d136f649e6da8e9029b8 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sun, 25 Sep 2016 18:27:20 -0400 Subject: [PATCH 22/33] pitch/yaw --- ent/qangle.go | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/ent/qangle.go b/ent/qangle.go index 38ffc49..e9745a5 100644 --- a/ent/qangle.go +++ b/ent/qangle.go @@ -2,28 +2,41 @@ package ent import ( "github.com/jordanorelli/hyperstone/bit" + "math" ) +type qangle struct{ pitch, yaw, roll float32 } + func qAngleType(spec *typeSpec, env *Env) tÿpe { if spec.typeName != "QAngle" { return nil } + if spec.encoder == "qangle_pitch_yaw" { + switch { + case spec.bits <= 0 || spec.bits > 32: + return typeError("qangle pitch_yaw has invalid bit size: %d", spec.bits) + case spec.bits == 32: + return typeFn(pitchYaw_t) + default: + return pitchYawAngles_t(spec.bits) + } + } switch spec.bits { case 0: Debug.Printf(" qangle type") return typeFn(func(r bit.Reader) (value, error) { - x, y, z := bit.ReadBool(r), bit.ReadBool(r), bit.ReadBool(r) - var v vector - if x { - v.x = bit.ReadCoord(r) + var q qangle + pitch, yaw, roll := bit.ReadBool(r), bit.ReadBool(r), bit.ReadBool(r) + if pitch { + q.pitch = bit.ReadCoord(r) } - if y { - v.y = bit.ReadCoord(r) + if yaw { + q.yaw = bit.ReadCoord(r) } - if z { - v.z = bit.ReadCoord(r) + if roll { + q.roll = bit.ReadCoord(r) } - return v, nil + return q, nil }) case 32: return nil @@ -31,3 +44,19 @@ func qAngleType(spec *typeSpec, env *Env) tÿpe { return nil } } + +func pitchYaw_t(r bit.Reader) (value, error) { + var q qangle + q.pitch = math.Float32frombits(uint32(r.ReadBits(32))) + q.yaw = math.Float32frombits(uint32(r.ReadBits(32))) + return q, r.Err() +} + +type pitchYawAngles_t uint + +func (t pitchYawAngles_t) read(r bit.Reader) (value, error) { + var q qangle + q.pitch = bit.ReadAngle(r, uint(t)) + q.yaw = bit.ReadAngle(r, uint(t)) + return q, r.Err() +} From bc32e7ac5b6240fc97c2b6bebc14c392bd82695a Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sun, 25 Sep 2016 19:13:00 -0400 Subject: [PATCH 23/33] bind fields to classes --- ent/class.go | 5 +++++ ent/env.go | 24 +++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/ent/class.go b/ent/class.go index 41ada50..5e8df3c 100644 --- a/ent/class.go +++ b/ent/class.go @@ -8,6 +8,11 @@ import ( type class struct { name string version int + fields []field +} + +func (c class) String() string { + return fmt.Sprintf("<%s.%d>", c.name, c.version) } func (c *class) read(r bit.Reader) (value, error) { diff --git a/ent/env.go b/ent/env.go index 9fa6e9e..dc5cbfb 100644 --- a/ent/env.go +++ b/ent/env.go @@ -1,8 +1,6 @@ package ent import ( - "fmt" - "github.com/golang/protobuf/proto" "github.com/jordanorelli/hyperstone/bit" @@ -40,9 +38,7 @@ func (e *Env) mergeSendTables(m *dota.CDemoSendTables) error { if err := e.parseFields(flat); err != nil { return wrap(err, "unable to parse serializer fields") } - if err := e.parseClasses(flat); err != nil { - return wrap(err, "unable to parse serializers") - } + e.fillClasses(flat) return nil } @@ -63,6 +59,9 @@ func (e *Env) stubClasses(flat *dota.CSVCMsg_FlattenedSerializer) { } } +// parses the type definitions for each field. some fields have types that +// refer to class types defined in the replay file. classes must be declared up +// front via stubclasses prior to parseFields working correctly. func (e *Env) parseFields(flat *dota.CSVCMsg_FlattenedSerializer) error { e.fields = make([]field, len(flat.GetFields())) for i, ff := range flat.GetFields() { @@ -74,8 +73,19 @@ func (e *Env) parseFields(flat *dota.CSVCMsg_FlattenedSerializer) error { return nil } -func (e *Env) parseClasses(flat *dota.CSVCMsg_FlattenedSerializer) error { - return fmt.Errorf("nope, not yet") +// associates each class with its list of fields. parseFields must be run +// before fillClasses in order for the field definitions to exist. +func (e *Env) fillClasses(flat *dota.CSVCMsg_FlattenedSerializer) { + for _, s := range flat.GetSerializers() { + name := e.symbol(int(s.GetSerializerNameSym())) + v := int(s.GetSerializerVersion()) + class := e.classes[name][v] + + class.fields = make([]field, len(s.GetFieldsIndex())) + for i, id := range s.GetFieldsIndex() { + class.fields[i] = e.fields[id] + } + } } func (e *Env) symbol(id int) string { return e.symbols[id] } From 6b2371a9407b865b94438cb15e1fb384b2849bb9 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Mon, 26 Sep 2016 10:47:56 -0400 Subject: [PATCH 24/33] store net ids --- ent/env.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++- main.go | 4 +--- stbl/table.go | 2 +- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/ent/env.go b/ent/env.go index dc5cbfb..86cba8c 100644 --- a/ent/env.go +++ b/ent/env.go @@ -5,19 +5,41 @@ import ( "github.com/jordanorelli/hyperstone/bit" "github.com/jordanorelli/hyperstone/dota" + "github.com/jordanorelli/hyperstone/stbl" ) type Env struct { symbols symbolTable source bit.BufReader classes map[string]classHistory + netIds map[int]string fields []field + strings *stbl.Dict +} + +func NewEnv() *Env { + e := &Env{strings: stbl.NewDict()} + e.strings.WatchTable("instancebaseline", e.syncBaselineTable) + return e } func (e *Env) Handle(m proto.Message) error { switch v := m.(type) { + case *dota.CSVCMsg_CreateStringTable: + _, err := e.strings.Create(v) + return err + + case *dota.CSVCMsg_UpdateStringTable: + return e.strings.Update(v) + case *dota.CDemoSendTables: - return e.mergeSendTables(v) + if err := e.mergeSendTables(v); err != nil { + return err + } + e.syncBaseline() + + case *dota.CDemoClassInfo: + e.mergeClassInfo(v) } return nil } @@ -26,6 +48,9 @@ func (e *Env) setSource(buf []byte) { e.source.SetSource(buf) } +// merges the "sendTables" message. "sendTables" is a deeply misleading name, +// but it's the name the structure is given in the protobufs. sendTables +// contains the type definitions that define our entity type system. func (e *Env) mergeSendTables(m *dota.CDemoSendTables) error { Debug.Printf("merge send tables") @@ -88,4 +113,30 @@ func (e *Env) fillClasses(flat *dota.CSVCMsg_FlattenedSerializer) { } } +func (e *Env) mergeClassInfo(m *dota.CDemoClassInfo) { + if e.netIds == nil { + e.netIds = make(map[int]string, len(m.GetClasses())) + } + for _, info := range m.GetClasses() { + id := info.GetClassId() + name := info.GetNetworkName() + table := info.GetTableName() + Debug.Printf("class info id: %d name: %s table: %s", id, name, table) + e.netIds[int(id)] = name + } +} + func (e *Env) symbol(id int) string { return e.symbols[id] } + +func (e *Env) syncBaseline() { + t := e.strings.TableForName("instancebaseline") + if t != nil { + e.syncBaselineTable(t) + } +} + +func (e *Env) syncBaselineTable(t *stbl.Table) { + if e.classes == nil { + Debug.Printf("syncBaselines skipped: classes are nil") + } +} diff --git a/main.go b/main.go index 454bed5..17e4c5f 100644 --- a/main.go +++ b/main.go @@ -142,10 +142,8 @@ func main() { handle = dumpClasses case "entities": ent.Debug = log.New(os.Stdout, "", 0) - sd := stbl.NewDict() - env := new(ent.Env) + env := ent.NewEnv() handle = func(m proto.Message) { - sd.Handle(m) if err := env.Handle(m); err != nil { bail(1, "%v", err) } diff --git a/stbl/table.go b/stbl/table.go index 7a76024..c6c5fea 100644 --- a/stbl/table.go +++ b/stbl/table.go @@ -61,7 +61,7 @@ func (t *Table) createEntries(br *bit.BufReader, n int) error { } } } - return br.Err() + return nil } func (t *Table) updateEntries(br *bit.BufReader, n int) error { From 79200cae38f793874c5d2d9015095bb097286d8f Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Wed, 28 Sep 2016 11:23:25 -0400 Subject: [PATCH 25/33] back to this thing again --- ent/class.go | 44 ++++++++++++++++++++++++------- ent/entity.go | 36 +++++++++++++++++++++++++ ent/env.go | 68 ++++++++++++++++++++++++++++++++++++++++-------- ent/selection.go | 29 +++++++++++++++++++++ ent/slotted.go | 7 +++++ ent/type.go | 9 ++++++- ent/vector.go | 9 ++++++- 7 files changed, 180 insertions(+), 22 deletions(-) create mode 100644 ent/entity.go create mode 100644 ent/slotted.go diff --git a/ent/class.go b/ent/class.go index 5e8df3c..9b0216a 100644 --- a/ent/class.go +++ b/ent/class.go @@ -16,21 +16,47 @@ func (c class) String() string { } func (c *class) read(r bit.Reader) (value, error) { - return nil, fmt.Errorf("fart") + bit.ReadBool(r) // ??? + return c.nü(), nil } -type classHistory map[int]*class +func (c *class) nü() entity { + return entity{class: c, slots: make([]value, len(c.fields))} +} + +type classHistory struct { + versions map[int]*class + oldest *class + newest *class +} + +func (h *classHistory) add(c *class) { + if h.oldest == nil || c.version < h.oldest.version { + h.oldest = c + } + if h.newest == nil || c.version > h.newest.version { + h.newest = c + } + if h.versions == nil { + h.versions = make(map[int]*class) + } + h.versions[c.version] = c +} + +func (h *classHistory) version(v int) *class { + if h.versions == nil { + return nil + } + return h.versions[v] +} func classType(spec *typeSpec, env *Env) tÿpe { if spec.serializer != "" { - h := env.classes[spec.serializer] - if h != nil { - class := h[spec.serializerV] - if class != nil { - return class - } - return typeError("class %s exists for spec serializer but can't find version %d", spec.serializer, spec.serializerV) + c := env.classVersion(spec.serializer, spec.serializerV) + if c != nil { + return c } + return typeError("unable to find class named %s with version %d", spec.serializer, spec.serializerV) } return nil } diff --git a/ent/entity.go b/ent/entity.go new file mode 100644 index 0000000..d4fdecb --- /dev/null +++ b/ent/entity.go @@ -0,0 +1,36 @@ +package ent + +import ( + "github.com/jordanorelli/hyperstone/bit" +) + +type entity struct { + class *class + slots []value +} + +func (e *entity) read(r bit.Reader) error { + Debug.Printf("entity %s read", e.className()) + sr := new(selectionReader) + selections, err := sr.readSelections(r, htree) + if err != nil { + return wrap(err, "entity of type %s failed to read selections", e.className()) + } + for _, s := range selections { + if err := s.fillSlots(e, r); err != nil { + return err + } + } + return nil +} + +func (e *entity) className() string { + if e.class != nil { + return e.class.name + } + return "" +} + +func (e *entity) slotType(i int) tÿpe { return e.class.fields[i].tÿpe } +func (e *entity) setSlotValue(i int, v value) { e.slots[i] = v } +func (e *entity) getSlotValue(i int) value { return e.slots[i] } diff --git a/ent/env.go b/ent/env.go index 86cba8c..180fd30 100644 --- a/ent/env.go +++ b/ent/env.go @@ -2,6 +2,7 @@ package ent import ( "github.com/golang/protobuf/proto" + "strconv" "github.com/jordanorelli/hyperstone/bit" "github.com/jordanorelli/hyperstone/dota" @@ -11,14 +12,17 @@ import ( type Env struct { symbols symbolTable source bit.BufReader - classes map[string]classHistory + classes map[string]*classHistory netIds map[int]string fields []field strings *stbl.Dict } func NewEnv() *Env { - e := &Env{strings: stbl.NewDict()} + e := &Env{ + classes: make(map[string]*classHistory), + strings: stbl.NewDict(), + } e.strings.WatchTable("instancebaseline", e.syncBaselineTable) return e } @@ -36,10 +40,10 @@ func (e *Env) Handle(m proto.Message) error { if err := e.mergeSendTables(v); err != nil { return err } - e.syncBaseline() case *dota.CDemoClassInfo: e.mergeClassInfo(v) + e.syncBaseline() } return nil } @@ -71,16 +75,17 @@ func (e *Env) mergeSendTables(m *dota.CDemoSendTables) error { // structs that fields may point to. func (e *Env) stubClasses(flat *dota.CSVCMsg_FlattenedSerializer) { serializers := flat.GetSerializers() - if e.classes == nil { - e.classes = make(map[string]classHistory, len(serializers)) - } for _, s := range serializers { name := e.symbol(int(s.GetSerializerNameSym())) v := int(s.GetSerializerVersion()) - if e.classes[name] == nil { - e.classes[name] = make(classHistory, 4) + c := &class{name: name, version: v} + Debug.Printf("new class: %s", c) + h := e.classes[name] + if h == nil { + h = new(classHistory) + e.classes[name] = h } - e.classes[name][v] = &class{name: name, version: v} + h.add(c) } } @@ -104,7 +109,7 @@ func (e *Env) fillClasses(flat *dota.CSVCMsg_FlattenedSerializer) { for _, s := range flat.GetSerializers() { name := e.symbol(int(s.GetSerializerNameSym())) v := int(s.GetSerializerVersion()) - class := e.classes[name][v] + class := e.classes[name].version(v) class.fields = make([]field, len(s.GetFieldsIndex())) for i, id := range s.GetFieldsIndex() { @@ -136,7 +141,48 @@ func (e *Env) syncBaseline() { } func (e *Env) syncBaselineTable(t *stbl.Table) { - if e.classes == nil { + if e.netIds == nil || len(e.netIds) == 0 { + Debug.Printf("syncBaselines skipped: net ids are nil") + } + if e.classes == nil || len(e.classes) == 0 { Debug.Printf("syncBaselines skipped: classes are nil") } + + r := new(bit.BufReader) + for _, entry := range t.Entries() { + netId, err := strconv.Atoi(entry.Key) + if err != nil { + Debug.Printf("syncBaselines ignored bad key %s: %v", err) + continue + } + className := e.netIds[netId] + if className == "" { + Debug.Printf("syncBaselines couldn't find class with net id %d", netId) + continue + } + c := e.class(className) + if c == nil { + Debug.Printf("syncBaselines couldn't find class named %s", className) + continue + } + Debug.Printf("syncBaselines key: %s className: %s", entry.Key, c.name) + ent := c.nü() + r.SetSource(entry.Value) + if err := ent.read(r); err != nil { + Debug.Printf("syncBaselines failed to fill an entity: %v", err) + } + } +} + +func (e *Env) class(name string) *class { + h := e.classes[name] + if h == nil { + return nil + } + return h.newest +} + +func (e *Env) classVersion(name string, version int) *class { + h := e.classes[name] + return h.version(version) } diff --git a/ent/selection.go b/ent/selection.go index 42f4ab7..12fd643 100644 --- a/ent/selection.go +++ b/ent/selection.go @@ -19,6 +19,35 @@ type selection struct { func (s selection) String() string { return fmt.Sprint(s.path()) } func (s selection) path() []int { return s.vals[:s.count] } +func (s selection) fillSlots(v slotted, r bit.Reader) error { + Debug.Printf("%v fill slots into %v", s, v) + return s.fillSlotsIter(0, v, r) +} + +func (s selection) fillSlotsIter(offset int, dest slotted, r bit.Reader) error { + slot := s.vals[offset] + if s.count-offset <= 0 { + return fmt.Errorf("unable to fill selection %v having count %d at offset %d", s, s.count, offset) + } + switch s.count - offset { + case 1: + t := dest.slotType(slot) + v, err := t.read(r) + if err != nil { + return fmt.Errorf("unable to fill selection: %v", err) + } + dest.setSlotValue(slot, v) + return nil + default: + v := dest.getSlotValue(slot) + vs, ok := v.(slotted) + if !ok { + return fmt.Errorf("destination is not slotted") + } + return s.fillSlotsIter(offset+1, vs, r) + } +} + // selectionReader reads a set of field selections off of the wire. the // selections are represented as arrays of slot positions to be traversed in // order to select an entity slot. diff --git a/ent/slotted.go b/ent/slotted.go new file mode 100644 index 0000000..db620a3 --- /dev/null +++ b/ent/slotted.go @@ -0,0 +1,7 @@ +package ent + +type slotted interface { + slotType(int) tÿpe + setSlotValue(int, value) + getSlotValue(int) value +} diff --git a/ent/type.go b/ent/type.go index 8803c0d..b52d271 100644 --- a/ent/type.go +++ b/ent/type.go @@ -34,7 +34,14 @@ func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { return nil } return coalesce(arrayType, atomType, floatType, handleType, qAngleType, - hSeqType, genericType, vectorType, classType) + hSeqType, genericType, vectorType, classType, unknownType) +} + +func unknownType(spec *typeSpec, env *Env) tÿpe { + Debug.Printf("Unknown Type: %v", spec) + return typeFn(func(r bit.Reader) (value, error) { + return bit.ReadVarInt(r), r.Err() + }) } // a type error is both an error and a type. It represents a type that we were diff --git a/ent/vector.go b/ent/vector.go index 2bdebca..a88d941 100644 --- a/ent/vector.go +++ b/ent/vector.go @@ -10,7 +10,14 @@ func vectorType(spec *typeSpec, env *Env) tÿpe { if spec.encoder != "" { return nil } - return vector_t{elem: floatType(spec, env)} + t := floatType(spec, env) + if _, ok := t.(error); ok { + return t + } + if t == nil { + return nil + } + return vector_t{elem: t} } type vector_t struct { From 4a0872f3ff2d36e10744a880e1ed9fd868d949bd Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Wed, 28 Sep 2016 11:51:34 -0400 Subject: [PATCH 26/33] types must have names --- ent/array.go | 3 ++ ent/atoms.go | 91 +++++++++++++++++++++++++++++++++----------------- ent/class.go | 4 +++ ent/float.go | 22 ++++++++---- ent/generic.go | 13 ++++++-- ent/handle.go | 9 +++-- ent/hseq.go | 9 +++-- ent/qangle.go | 35 ++++++++++--------- ent/type.go | 21 ++++++++---- ent/vector.go | 5 +++ 10 files changed, 144 insertions(+), 68 deletions(-) diff --git a/ent/array.go b/ent/array.go index 2a4a692..537d328 100644 --- a/ent/array.go +++ b/ent/array.go @@ -1,6 +1,7 @@ package ent import ( + "fmt" "strconv" "strings" @@ -48,6 +49,8 @@ type array_t struct { count int } +func (t array_t) typeName() string { return fmt.Sprintf("array:%s", t.elem.typeName()) } + func (t array_t) read(r bit.Reader) (value, error) { var err error v := make(array, t.count) diff --git a/ent/atoms.go b/ent/atoms.go index af3e921..2d2a488 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -4,49 +4,78 @@ import ( "github.com/jordanorelli/hyperstone/bit" ) -var atom_types = map[string]typeFn{ - "bool": func(r bit.Reader) (value, error) { - return bit.ReadBool(r), r.Err() +var atom_types = []typeLiteral{ + { + "bool", + func(r bit.Reader) (value, error) { + return bit.ReadBool(r), r.Err() + }, }, - "uint8": func(r bit.Reader) (value, error) { - // TODO: bounds check here - return uint8(bit.ReadVarInt(r)), r.Err() + { + "uint8", + func(r bit.Reader) (value, error) { + // TODO: bounds check here + return uint8(bit.ReadVarInt(r)), r.Err() + }, }, - "uint16": func(r bit.Reader) (value, error) { - // TODO: bounds check here - return uint16(bit.ReadVarInt(r)), r.Err() + { + "uint16", + func(r bit.Reader) (value, error) { + // TODO: bounds check here + return uint16(bit.ReadVarInt(r)), r.Err() + }, }, - "uint32": func(r bit.Reader) (value, error) { - return bit.ReadVarInt32(r), r.Err() + { + "uint32", + func(r bit.Reader) (value, error) { + return bit.ReadVarInt32(r), r.Err() + }, }, - "uint64": func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r), r.Err() + { + "uint64", + func(r bit.Reader) (value, error) { + return bit.ReadVarInt(r), r.Err() + }, }, - "int8": func(r bit.Reader) (value, error) { - // TODO: bounds check here - return int8(bit.ReadZigZag32(r)), r.Err() + { + "int8", + func(r bit.Reader) (value, error) { + // TODO: bounds check here + return int8(bit.ReadZigZag32(r)), r.Err() + }, }, - "int32": func(r bit.Reader) (value, error) { - return bit.ReadZigZag32(r), r.Err() + { + "int32", + func(r bit.Reader) (value, error) { + return bit.ReadZigZag32(r), r.Err() + }, }, - "CUtlStringToken": func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r), r.Err() + { + "CUtlStringToken", + func(r bit.Reader) (value, error) { + return bit.ReadVarInt(r), r.Err() + }, }, - "Color": func(r bit.Reader) (value, error) { - u := bit.ReadVarInt(r) - return color{ - r: uint8(u >> 6 & 0xff), - g: uint8(u >> 4 & 0xff), - b: uint8(u >> 2 & 0xff), - a: uint8(u >> 0 & 0xff), - }, r.Err() + { + "Color", + func(r bit.Reader) (value, error) { + u := bit.ReadVarInt(r) + return color{ + r: uint8(u >> 6 & 0xff), + g: uint8(u >> 4 & 0xff), + b: uint8(u >> 2 & 0xff), + a: uint8(u >> 0 & 0xff), + }, r.Err() + }, }, } func atomType(spec *typeSpec, env *Env) tÿpe { - if t, ok := atom_types[spec.typeName]; ok { - Debug.Printf(" atom type") - return t + for _, t := range atom_types { + if t.typeName() == spec.typeName { + Debug.Printf(" atom type: %s", t.typeName()) + return t + } } return nil } diff --git a/ent/class.go b/ent/class.go index 9b0216a..8aa1ca7 100644 --- a/ent/class.go +++ b/ent/class.go @@ -15,6 +15,10 @@ func (c class) String() string { return fmt.Sprintf("<%s.%d>", c.name, c.version) } +func (c class) typeName() string { + return fmt.Sprintf("class:%s", c.name) +} + func (c *class) read(r bit.Reader) (value, error) { bit.ReadBool(r) // ??? return c.nü(), nil diff --git a/ent/float.go b/ent/float.go index 5601ba6..d5dea25 100644 --- a/ent/float.go +++ b/ent/float.go @@ -23,9 +23,12 @@ func floatType(spec *typeSpec, env *Env) tÿpe { } if spec.encoder == "coord" { Debug.Printf(" coord float type") - return typeFn(func(r bit.Reader) (value, error) { - return bit.ReadCoord(r), r.Err() - }) + return typeLiteral{ + "float:coord", + func(r bit.Reader) (value, error) { + return bit.ReadCoord(r), r.Err() + }, + } } if spec.serializer == "simulationtime" { return nil @@ -33,10 +36,13 @@ func floatType(spec *typeSpec, env *Env) tÿpe { switch spec.bits { case 0, 32: Debug.Printf(" std float type") - return typeFn(func(r bit.Reader) (value, error) { - // TODO: check uint32 overflow here? - return math.Float32frombits(uint32(r.ReadBits(32))), r.Err() - }) + return typeLiteral{ + "float:std", + func(r bit.Reader) (value, error) { + // TODO: check uint32 overflow here? + return math.Float32frombits(uint32(r.ReadBits(32))), r.Err() + }, + } default: return qFloatType(spec, env) } @@ -81,6 +87,8 @@ type qfloat_t struct { special *float32 } +func (t qfloat_t) typeName() string { return "qfloat" } + func (t qfloat_t) read(r bit.Reader) (value, error) { if t.special != nil && bit.ReadBool(r) { return *t.special, nil diff --git a/ent/generic.go b/ent/generic.go index a37a212..50ace5e 100644 --- a/ent/generic.go +++ b/ent/generic.go @@ -24,9 +24,12 @@ func genericType(spec *typeSpec, env *Env) tÿpe { switch genericName { case "CHandle", "CStrongHandle": - return typeFn(func(r bit.Reader) (value, error) { - return handle(bit.ReadVarInt(r)), r.Err() - }) + return typeLiteral{ + fmt.Sprintf("handle:%s", genericName), + func(r bit.Reader) (value, error) { + return handle(bit.ReadVarInt(r)), r.Err() + }, + } case "CUtlVector": return cutl_vector_t{elem} default: @@ -72,6 +75,10 @@ type cutl_vector_t struct { elem tÿpe } +func (t cutl_vector_t) typeName() string { + return fmt.Sprintf("vector:%s", t.elem.typeName()) +} + func (t cutl_vector_t) read(r bit.Reader) (value, error) { count := bit.ReadVarInt32(r) return make(array, count), r.Err() diff --git a/ent/handle.go b/ent/handle.go index 5a1bba9..56110ab 100644 --- a/ent/handle.go +++ b/ent/handle.go @@ -14,7 +14,10 @@ func handleType(spec *typeSpec, env *Env) tÿpe { } Debug.Printf(" handle type") - return typeFn(func(r bit.Reader) (value, error) { - return handle(bit.ReadVarInt(r)), r.Err() - }) + return typeLiteral{ + "handle:CGameSceneNodeHandle", + func(r bit.Reader) (value, error) { + return handle(bit.ReadVarInt(r)), r.Err() + }, + } } diff --git a/ent/hseq.go b/ent/hseq.go index c554eb7..d4ae875 100644 --- a/ent/hseq.go +++ b/ent/hseq.go @@ -10,7 +10,10 @@ func hSeqType(spec *typeSpec, env *Env) tÿpe { } Debug.Printf(" hsequence type") - return typeFn(func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r) - 1, r.Err() - }) + return typeLiteral{ + "HSequence", + func(r bit.Reader) (value, error) { + return bit.ReadVarInt(r) - 1, r.Err() + }, + } } diff --git a/ent/qangle.go b/ent/qangle.go index e9745a5..0134d30 100644 --- a/ent/qangle.go +++ b/ent/qangle.go @@ -16,7 +16,7 @@ func qAngleType(spec *typeSpec, env *Env) tÿpe { case spec.bits <= 0 || spec.bits > 32: return typeError("qangle pitch_yaw has invalid bit size: %d", spec.bits) case spec.bits == 32: - return typeFn(pitchYaw_t) + return typeLiteral{"qangle:pitchYaw", pitchYaw_t} default: return pitchYawAngles_t(spec.bits) } @@ -24,20 +24,23 @@ func qAngleType(spec *typeSpec, env *Env) tÿpe { switch spec.bits { case 0: Debug.Printf(" qangle type") - return typeFn(func(r bit.Reader) (value, error) { - var q qangle - pitch, yaw, roll := bit.ReadBool(r), bit.ReadBool(r), bit.ReadBool(r) - if pitch { - q.pitch = bit.ReadCoord(r) - } - if yaw { - q.yaw = bit.ReadCoord(r) - } - if roll { - q.roll = bit.ReadCoord(r) - } - return q, nil - }) + return typeLiteral{ + "qangle", + func(r bit.Reader) (value, error) { + var q qangle + pitch, yaw, roll := bit.ReadBool(r), bit.ReadBool(r), bit.ReadBool(r) + if pitch { + q.pitch = bit.ReadCoord(r) + } + if yaw { + q.yaw = bit.ReadCoord(r) + } + if roll { + q.roll = bit.ReadCoord(r) + } + return q, nil + }, + } case 32: return nil default: @@ -54,6 +57,8 @@ func pitchYaw_t(r bit.Reader) (value, error) { type pitchYawAngles_t uint +func (t pitchYawAngles_t) typeName() string { return "qangle:pitchYawAngles" } + func (t pitchYawAngles_t) read(r bit.Reader) (value, error) { var q qangle q.pitch = bit.ReadAngle(r, uint(t)) diff --git a/ent/type.go b/ent/type.go index b52d271..7238016 100644 --- a/ent/type.go +++ b/ent/type.go @@ -9,11 +9,16 @@ import ( type tÿpe interface { read(bit.Reader) (value, error) + typeName() string } -type typeFn func(bit.Reader) (value, error) +type typeLiteral struct { + name string + readFn func(r bit.Reader) (value, error) +} -func (fn typeFn) read(r bit.Reader) (value, error) { return fn(r) } +func (t typeLiteral) typeName() string { return t.name } +func (t typeLiteral) read(r bit.Reader) (value, error) { return t.readFn(r) } type typeParseFn func(*typeSpec, *Env) tÿpe @@ -39,9 +44,12 @@ func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { func unknownType(spec *typeSpec, env *Env) tÿpe { Debug.Printf("Unknown Type: %v", spec) - return typeFn(func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r), r.Err() - }) + return typeLiteral{ + name: fmt.Sprintf("unknown:%s", spec.typeName), + readFn: func(r bit.Reader) (value, error) { + return bit.ReadVarInt(r), r.Err() + }, + } } // a type error is both an error and a type. It represents a type that we were @@ -54,7 +62,8 @@ func typeError(t string, args ...interface{}) tÿpe { type error_t string -func (e error_t) Error() string { return string(e) } +func (e error_t) typeName() string { return "error" } +func (e error_t) Error() string { return string(e) } func (e error_t) read(r bit.Reader) (value, error) { return nil, fmt.Errorf("type error: %s", string(e)) } diff --git a/ent/vector.go b/ent/vector.go index a88d941..c8f7e57 100644 --- a/ent/vector.go +++ b/ent/vector.go @@ -1,6 +1,7 @@ package ent import ( + "fmt" "github.com/jordanorelli/hyperstone/bit" ) @@ -24,6 +25,10 @@ type vector_t struct { elem tÿpe } +func (t vector_t) typeName() string { + return fmt.Sprintf("vector:%s", t.elem.typeName()) +} + func (t vector_t) read(r bit.Reader) (value, error) { var err error var v interface{} From b5e52809130560aed8e97b72b8eeb4bc84838fb7 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Wed, 28 Sep 2016 21:32:41 -0400 Subject: [PATCH 27/33] values read themselves --- ent/array.go | 30 +++-- ent/atoms.go | 291 +++++++++++++++++++++++++++++++++++++---------- ent/class.go | 4 +- ent/entity.go | 7 ++ ent/float.go | 77 ++++++++++--- ent/generic.go | 30 +++-- ent/handle.go | 30 +++-- ent/hseq.go | 29 +++-- ent/qangle.go | 98 +++++++++++----- ent/selection.go | 20 ++-- ent/slotted.go | 2 + ent/type.go | 44 ++++--- ent/value.go | 10 +- ent/vector.go | 52 ++++++--- 14 files changed, 545 insertions(+), 179 deletions(-) diff --git a/ent/array.go b/ent/array.go index 537d328..9e332cb 100644 --- a/ent/array.go +++ b/ent/array.go @@ -20,7 +20,7 @@ func arrayType(spec *typeSpec, env *Env) tÿpe { elemSpec := *spec elemSpec.typeName = elemName elemType := parseTypeSpec(&elemSpec, env) - return array_t{elemType, count} + return &array_t{elemType, count} } func parseArrayName(s string) (string, int) { @@ -49,18 +49,28 @@ type array_t struct { count int } +func (t *array_t) nü() value { return array{t: t, slots: make([]value, t.count)} } func (t array_t) typeName() string { return fmt.Sprintf("array:%s", t.elem.typeName()) } -func (t array_t) read(r bit.Reader) (value, error) { - var err error - v := make(array, t.count) - for i := range v { - v[i], err = t.elem.read(r) - if err != nil { - return nil, wrap(err, "array read error at index %d", i) +type array struct { + t *array_t + slots []value +} + +func (a array) tÿpe() tÿpe { return a.t } + +func (a array) read(r bit.Reader) error { + for i := range a.slots { + if a.slots[i] == nil { + a.slots[i] = a.t.elem.nü() + } + if err := a.slots[i].read(r); err != nil { + return wrap(err, "array read error at index %d", i) } } - return v, r.Err() + return r.Err() } -type array []value +func (a array) String() string { + return fmt.Sprintf("%s%v", a.t.typeName(), a.slots) +} diff --git a/ent/atoms.go b/ent/atoms.go index 2d2a488..2eaf83f 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -1,75 +1,254 @@ package ent import ( + "fmt" "github.com/jordanorelli/hyperstone/bit" + "strconv" ) -var atom_types = []typeLiteral{ - { - "bool", - func(r bit.Reader) (value, error) { - return bit.ReadBool(r), r.Err() - }, +// ------------------------------------------------------------------------------ +// bool +// ------------------------------------------------------------------------------ + +var bool_t = &typeLiteral{ + name: "bool", + newFn: func() value { + return new(bool_v) }, - { - "uint8", - func(r bit.Reader) (value, error) { - // TODO: bounds check here - return uint8(bit.ReadVarInt(r)), r.Err() - }, +} + +type bool_v bool + +func (v bool_v) tÿpe() tÿpe { return bool_t } + +func (v *bool_v) read(r bit.Reader) error { + *v = bool_v(bit.ReadBool(r)) + return r.Err() +} + +func (v bool_v) String() string { + if v { + return "true" + } + return "false" +} + +// ------------------------------------------------------------------------------ +// uint8 +// ------------------------------------------------------------------------------ + +var uint8_t = &typeLiteral{ + name: "uint8", + newFn: func() value { + return new(uint8_v) }, - { - "uint16", - func(r bit.Reader) (value, error) { - // TODO: bounds check here - return uint16(bit.ReadVarInt(r)), r.Err() - }, +} + +type uint8_v uint8 + +func (v uint8_v) tÿpe() tÿpe { return uint8_t } +func (v *uint8_v) read(r bit.Reader) error { + u := bit.ReadVarInt(r) + if u > 1<<8-1 { + return fmt.Errorf("uint8 overflow: %d", u) + } + *v = uint8_v(u) + return r.Err() +} + +func (v uint8_v) String() string { + return strconv.FormatUint(uint64(v), 10) +} + +// ------------------------------------------------------------------------------ +// uint16 +// ------------------------------------------------------------------------------ + +var uint16_t = &typeLiteral{ + name: "uint16", + newFn: func() value { + return new(uint16_v) }, - { - "uint32", - func(r bit.Reader) (value, error) { - return bit.ReadVarInt32(r), r.Err() - }, +} + +type uint16_v uint16 + +func (v uint16_v) tÿpe() tÿpe { return uint16_t } +func (v *uint16_v) read(r bit.Reader) error { + u := bit.ReadVarInt(r) + if u > 1<<16-1 { + return fmt.Errorf("uint16 overflow: %d", u) + } + *v = uint16_v(u) + return r.Err() +} + +func (v uint16_v) String() string { + return strconv.FormatUint(uint64(v), 10) +} + +// ------------------------------------------------------------------------------ +// uint32 +// ------------------------------------------------------------------------------ + +var uint32_t = &typeLiteral{ + name: "uint32", + newFn: func() value { + return new(uint32_v) }, - { - "uint64", - func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r), r.Err() - }, +} + +type uint32_v uint32 + +func (v uint32_v) tÿpe() tÿpe { return uint32_t } +func (v *uint32_v) read(r bit.Reader) error { + u := bit.ReadVarInt(r) + if u > 1<<32-1 { + return fmt.Errorf("uint32 overflow: %d", u) + } + *v = uint32_v(u) + return r.Err() +} + +func (v uint32_v) String() string { + return strconv.FormatUint(uint64(v), 0) +} + +// ------------------------------------------------------------------------------ +// uint64 +// ------------------------------------------------------------------------------ + +var uint64_t = &typeLiteral{ + name: "uint64", + newFn: func() value { + return new(uint64_v) }, - { - "int8", - func(r bit.Reader) (value, error) { - // TODO: bounds check here - return int8(bit.ReadZigZag32(r)), r.Err() - }, +} + +type uint64_v uint64 + +func (v uint64_v) tÿpe() tÿpe { return uint64_t } +func (v *uint64_v) read(r bit.Reader) error { + *v = uint64_v(bit.ReadVarInt(r)) + return r.Err() +} + +func (v uint64_v) String() string { + return strconv.FormatUint(uint64(v), 10) +} + +// ------------------------------------------------------------------------------ +// int8 +// ------------------------------------------------------------------------------ + +var int8_t = &typeLiteral{ + name: "int8", + newFn: func() value { + return new(int8_v) }, - { - "int32", - func(r bit.Reader) (value, error) { - return bit.ReadZigZag32(r), r.Err() - }, +} + +type int8_v int8 + +func (v int8_v) tÿpe() tÿpe { return int8_t } +func (v *int8_v) read(r bit.Reader) error { + // TODO: bounds check here? + *v = int8_v(bit.ReadZigZag32(r)) + return r.Err() +} + +func (v int8_v) String() string { + return strconv.FormatInt(int64(v), 10) +} + +// ------------------------------------------------------------------------------ +// int32 +// ------------------------------------------------------------------------------ + +var int32_t = &typeLiteral{ + name: "int32", + newFn: func() value { + return new(int32_v) }, - { - "CUtlStringToken", - func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r), r.Err() - }, +} + +type int32_v int32 + +func (v int32_v) tÿpe() tÿpe { return int32_t } +func (v *int32_v) read(r bit.Reader) error { + *v = int32_v(bit.ReadZigZag32(r)) + return r.Err() +} + +func (v int32_v) String() string { + return strconv.FormatInt(int64(v), 10) +} + +// ------------------------------------------------------------------------------ +// CUtlStringToken +// +// weirdly, this type isn't a string; it's actually a number. The number +// presumably indicates some value on a symbol table. +// ------------------------------------------------------------------------------ + +var stringToken_t = &typeLiteral{ + name: "CUtlStringToken", + newFn: func() value { + return new(stringToken_v) }, - { - "Color", - func(r bit.Reader) (value, error) { - u := bit.ReadVarInt(r) - return color{ - r: uint8(u >> 6 & 0xff), - g: uint8(u >> 4 & 0xff), - b: uint8(u >> 2 & 0xff), - a: uint8(u >> 0 & 0xff), - }, r.Err() - }, +} + +type stringToken_v uint64 + +func (v stringToken_v) tÿpe() tÿpe { return stringToken_t } +func (v *stringToken_v) read(r bit.Reader) error { + *v = stringToken_v(bit.ReadVarInt(r)) + return r.Err() +} + +func (v stringToken_v) String() string { + return fmt.Sprintf("token:%d", v) +} + +// ------------------------------------------------------------------------------ +// Color +// ------------------------------------------------------------------------------ + +var color_t = &typeLiteral{ + name: "Color", + newFn: func() value { + return new(color) }, } +type color struct{ r, g, b, a uint8 } + +func (c color) tÿpe() tÿpe { return color_t } +func (c *color) read(r bit.Reader) error { + u := bit.ReadVarInt(r) + c.r = uint8(u >> 6 & 0xff) + c.g = uint8(u >> 4 & 0xff) + c.b = uint8(u >> 2 & 0xff) + c.a = uint8(u >> 0 & 0xff) + return r.Err() +} + +func (c color) String() string { + return fmt.Sprintf("#%x%x%x%x", c.r, c.g, c.b, c.a) +} + +var atom_types = []tÿpe{ + bool_t, + uint8_t, + uint16_t, + uint32_t, + uint64_t, + int8_t, + int32_t, + stringToken_t, + color_t, +} + func atomType(spec *typeSpec, env *Env) tÿpe { for _, t := range atom_types { if t.typeName() == spec.typeName { @@ -79,5 +258,3 @@ func atomType(spec *typeSpec, env *Env) tÿpe { } return nil } - -type color struct{ r, g, b, a uint8 } diff --git a/ent/class.go b/ent/class.go index 8aa1ca7..6ac14fc 100644 --- a/ent/class.go +++ b/ent/class.go @@ -24,8 +24,8 @@ func (c *class) read(r bit.Reader) (value, error) { return c.nü(), nil } -func (c *class) nü() entity { - return entity{class: c, slots: make([]value, len(c.fields))} +func (c *class) nü() value { + return &entity{class: c, slots: make([]value, len(c.fields))} } type classHistory struct { diff --git a/ent/entity.go b/ent/entity.go index d4fdecb..d93ea20 100644 --- a/ent/entity.go +++ b/ent/entity.go @@ -1,6 +1,7 @@ package ent import ( + "fmt" "github.com/jordanorelli/hyperstone/bit" ) @@ -31,6 +32,12 @@ func (e *entity) className() string { return "" } +func (e *entity) String() string { + return fmt.Sprintf("%s{%v}", e.class.typeName(), e.slots) +} + +func (e *entity) tÿpe() tÿpe { return e.class } func (e *entity) slotType(i int) tÿpe { return e.class.fields[i].tÿpe } +func (e *entity) slotName(i int) string { return e.class.fields[i].name } func (e *entity) setSlotValue(i int, v value) { e.slots[i] = v } func (e *entity) getSlotValue(i int) value { return e.slots[i] } diff --git a/ent/float.go b/ent/float.go index d5dea25..6af3cd6 100644 --- a/ent/float.go +++ b/ent/float.go @@ -2,6 +2,7 @@ package ent import ( "math" + "strconv" "github.com/jordanorelli/hyperstone/bit" ) @@ -23,12 +24,7 @@ func floatType(spec *typeSpec, env *Env) tÿpe { } if spec.encoder == "coord" { Debug.Printf(" coord float type") - return typeLiteral{ - "float:coord", - func(r bit.Reader) (value, error) { - return bit.ReadCoord(r), r.Err() - }, - } + return coord_t } if spec.serializer == "simulationtime" { return nil @@ -36,18 +32,50 @@ func floatType(spec *typeSpec, env *Env) tÿpe { switch spec.bits { case 0, 32: Debug.Printf(" std float type") - return typeLiteral{ - "float:std", - func(r bit.Reader) (value, error) { - // TODO: check uint32 overflow here? - return math.Float32frombits(uint32(r.ReadBits(32))), r.Err() - }, - } + return float_t default: return qFloatType(spec, env) } } +var coord_t = &typeLiteral{ + name: "coord", + newFn: func() value { + return new(coord_v) + }, +} + +type coord_v float32 + +func (v coord_v) tÿpe() tÿpe { return coord_t } +func (v *coord_v) read(r bit.Reader) error { + *v = coord_v(bit.ReadCoord(r)) + return r.Err() +} + +func (v coord_v) String() string { + return strconv.FormatFloat(float64(v), 'f', 3, 32) +} + +var float_t = &typeLiteral{ + name: "float", + newFn: func() value { + return new(float_v) + }, +} + +type float_v float32 + +func (v float_v) tÿpe() tÿpe { return float_t } +func (v *float_v) read(r bit.Reader) error { + *v = float_v(math.Float32frombits(uint32(r.ReadBits(32)))) + return r.Err() +} + +func (v float_v) String() string { + return strconv.FormatFloat(float64(v), 'f', 3, 32) +} + func qFloatType(spec *typeSpec, env *Env) tÿpe { if spec.bits < 0 { return typeError("quantized float has invalid negative bit count specifier") @@ -56,7 +84,7 @@ func qFloatType(spec *typeSpec, env *Env) tÿpe { return typeError("quantized float has invalid negative range") } - t := qfloat_t{typeSpec: *spec} + t := &qfloat_t{typeSpec: *spec} t.span = t.high - t.low t.intervals = uint(1<", v.t.typeName(), v.slots) } diff --git a/ent/handle.go b/ent/handle.go index 56110ab..3ea0f10 100644 --- a/ent/handle.go +++ b/ent/handle.go @@ -1,12 +1,32 @@ package ent import ( + "fmt" + "github.com/jordanorelli/hyperstone/bit" ) +type handle_t string + +func (t handle_t) typeName() string { return string(t) } +func (t *handle_t) nü() value { return &handle{t: t} } + // a handle represents a soft pointer to an entity. handles are represented by // IDs and can cross the client-server divide. -type handle int +type handle struct { + t tÿpe + id uint64 +} + +func (h handle) tÿpe() tÿpe { return h.t } +func (h *handle) read(r bit.Reader) error { + h.id = bit.ReadVarInt(r) + return r.Err() +} + +func (h handle) String() string { + return fmt.Sprintf("handle<%s>: %d", h.t.typeName(), h.id) +} func handleType(spec *typeSpec, env *Env) tÿpe { if spec.typeName != "CGameSceneNodeHandle" { @@ -14,10 +34,6 @@ func handleType(spec *typeSpec, env *Env) tÿpe { } Debug.Printf(" handle type") - return typeLiteral{ - "handle:CGameSceneNodeHandle", - func(r bit.Reader) (value, error) { - return handle(bit.ReadVarInt(r)), r.Err() - }, - } + t := handle_t(spec.typeName) + return &t } diff --git a/ent/hseq.go b/ent/hseq.go index d4ae875..e9d3760 100644 --- a/ent/hseq.go +++ b/ent/hseq.go @@ -1,19 +1,34 @@ package ent import ( + "fmt" + "github.com/jordanorelli/hyperstone/bit" ) +var hseq_t = &typeLiteral{ + name: "HSequence", + newFn: func() value { + return new(hseq_v) + }, +} + +type hseq_v uint64 + +func (v hseq_v) tÿpe() tÿpe { return hseq_t } +func (v *hseq_v) read(r bit.Reader) error { + *v = hseq_v(bit.ReadVarInt(r) - 1) + return r.Err() +} + +func (v hseq_v) String() string { + return fmt.Sprintf("hseq:%d", uint64(v)) +} + func hSeqType(spec *typeSpec, env *Env) tÿpe { if spec.typeName != "HSequence" { return nil } - Debug.Printf(" hsequence type") - return typeLiteral{ - "HSequence", - func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r) - 1, r.Err() - }, - } + return hseq_t } diff --git a/ent/qangle.go b/ent/qangle.go index 0134d30..a960c9f 100644 --- a/ent/qangle.go +++ b/ent/qangle.go @@ -1,12 +1,40 @@ package ent import ( - "github.com/jordanorelli/hyperstone/bit" + "fmt" "math" + + "github.com/jordanorelli/hyperstone/bit" ) +var qangle_t = &typeLiteral{ + name: "qangle", + newFn: func() value { + return new(qangle) + }, +} + type qangle struct{ pitch, yaw, roll float32 } +func (q qangle) tÿpe() tÿpe { return qangle_t } +func (q *qangle) read(r bit.Reader) error { + pitch, yaw, roll := bit.ReadBool(r), bit.ReadBool(r), bit.ReadBool(r) + if pitch { + q.pitch = bit.ReadCoord(r) + } + if yaw { + q.yaw = bit.ReadCoord(r) + } + if roll { + q.roll = bit.ReadCoord(r) + } + return r.Err() +} + +func (q qangle) String() string { + return fmt.Sprintf("qangle{%f %f %f}", q.pitch, q.yaw, q.roll) +} + func qAngleType(spec *typeSpec, env *Env) tÿpe { if spec.typeName != "QAngle" { return nil @@ -16,31 +44,16 @@ func qAngleType(spec *typeSpec, env *Env) tÿpe { case spec.bits <= 0 || spec.bits > 32: return typeError("qangle pitch_yaw has invalid bit size: %d", spec.bits) case spec.bits == 32: - return typeLiteral{"qangle:pitchYaw", pitchYaw_t} + return pitchYaw_t default: - return pitchYawAngles_t(spec.bits) + t := pitchYawAngles_t(spec.bits) + return &t } } switch spec.bits { case 0: Debug.Printf(" qangle type") - return typeLiteral{ - "qangle", - func(r bit.Reader) (value, error) { - var q qangle - pitch, yaw, roll := bit.ReadBool(r), bit.ReadBool(r), bit.ReadBool(r) - if pitch { - q.pitch = bit.ReadCoord(r) - } - if yaw { - q.yaw = bit.ReadCoord(r) - } - if roll { - q.roll = bit.ReadCoord(r) - } - return q, nil - }, - } + return qangle_t case 32: return nil default: @@ -48,20 +61,45 @@ func qAngleType(spec *typeSpec, env *Env) tÿpe { } } -func pitchYaw_t(r bit.Reader) (value, error) { - var q qangle - q.pitch = math.Float32frombits(uint32(r.ReadBits(32))) - q.yaw = math.Float32frombits(uint32(r.ReadBits(32))) - return q, r.Err() +var pitchYaw_t = &typeLiteral{ + name: "qangle:pitchYaw", + newFn: func() value { + return new(pitchYaw_v) + }, +} + +type pitchYaw_v qangle + +func (v pitchYaw_v) tÿpe() tÿpe { return pitchYaw_t } +func (v *pitchYaw_v) read(r bit.Reader) error { + v.pitch = math.Float32frombits(uint32(r.ReadBits(32))) + v.yaw = math.Float32frombits(uint32(r.ReadBits(32))) + return r.Err() +} + +func (v pitchYaw_v) String() string { + return fmt.Sprintf("qangle:pitchYaw{%f %f}", v.pitch, v.yaw) } type pitchYawAngles_t uint func (t pitchYawAngles_t) typeName() string { return "qangle:pitchYawAngles" } +func (t *pitchYawAngles_t) nü() value { + return &pitchYawAngles_v{t: t} +} + +type pitchYawAngles_v struct { + t *pitchYawAngles_t + qangle +} + +func (v pitchYawAngles_v) tÿpe() tÿpe { return v.t } +func (v *pitchYawAngles_v) read(r bit.Reader) error { + v.pitch = bit.ReadAngle(r, uint(*v.t)) + v.yaw = bit.ReadAngle(r, uint(*v.t)) + return r.Err() +} -func (t pitchYawAngles_t) read(r bit.Reader) (value, error) { - var q qangle - q.pitch = bit.ReadAngle(r, uint(t)) - q.yaw = bit.ReadAngle(r, uint(t)) - return q, r.Err() +func (v pitchYawAngles_v) String() string { + return fmt.Sprintf("qangle:pitchYawAngles{%f %f}", v.pitch, v.yaw) } diff --git a/ent/selection.go b/ent/selection.go index 12fd643..7f77eb6 100644 --- a/ent/selection.go +++ b/ent/selection.go @@ -20,31 +20,35 @@ func (s selection) String() string { return fmt.Sprint(s.path()) } func (s selection) path() []int { return s.vals[:s.count] } func (s selection) fillSlots(v slotted, r bit.Reader) error { - Debug.Printf("%v fill slots into %v", s, v) - return s.fillSlotsIter(0, v, r) + return s.fillSlotsIter(0, v, v.tÿpe().typeName(), r) } -func (s selection) fillSlotsIter(offset int, dest slotted, r bit.Reader) error { +func (s selection) fillSlotsIter(offset int, dest slotted, path string, r bit.Reader) error { slot := s.vals[offset] if s.count-offset <= 0 { return fmt.Errorf("unable to fill selection %v having count %d at offset %d", s, s.count, offset) } switch s.count - offset { case 1: - t := dest.slotType(slot) - v, err := t.read(r) - if err != nil { + v := dest.slotType(slot).nü() + if err := v.read(r); err != nil { return fmt.Errorf("unable to fill selection: %v", err) } + old := dest.getSlotValue(slot) dest.setSlotValue(slot, v) + Debug.Printf("%v %s.%s (%s) %v -> %v", s, path, dest.slotName(slot), dest.slotType(slot).typeName(), old, v) return nil default: v := dest.getSlotValue(slot) + if v == nil { + v = dest.slotType(slot).nü() + dest.setSlotValue(slot, v) + } vs, ok := v.(slotted) if !ok { - return fmt.Errorf("destination is not slotted") + return fmt.Errorf("dest isn't slotted") } - return s.fillSlotsIter(offset+1, vs, r) + return s.fillSlotsIter(offset+1, vs, fmt.Sprintf("%s.%s", path, dest.slotName(slot)), r) } } diff --git a/ent/slotted.go b/ent/slotted.go index db620a3..cb1a088 100644 --- a/ent/slotted.go +++ b/ent/slotted.go @@ -1,7 +1,9 @@ package ent type slotted interface { + value slotType(int) tÿpe + slotName(int) string setSlotValue(int, value) getSlotValue(int) value } diff --git a/ent/type.go b/ent/type.go index 7238016..3060680 100644 --- a/ent/type.go +++ b/ent/type.go @@ -8,17 +8,17 @@ import ( ) type tÿpe interface { - read(bit.Reader) (value, error) + nü() value typeName() string } type typeLiteral struct { - name string - readFn func(r bit.Reader) (value, error) + name string + newFn func() value } -func (t typeLiteral) typeName() string { return t.name } -func (t typeLiteral) read(r bit.Reader) (value, error) { return t.readFn(r) } +func (t typeLiteral) nü() value { return t.newFn() } +func (t typeLiteral) typeName() string { return t.name } type typeParseFn func(*typeSpec, *Env) tÿpe @@ -42,14 +42,32 @@ func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { hSeqType, genericType, vectorType, classType, unknownType) } +type unknown_t string + +func (t unknown_t) typeName() string { return string(t) } +func (t *unknown_t) nü() value { + return &unknown_v{t: t} +} + +type unknown_v struct { + t tÿpe + v uint64 +} + +func (v unknown_v) tÿpe() tÿpe { return v.t } +func (v *unknown_v) read(r bit.Reader) error { + v.v = bit.ReadVarInt(r) + return r.Err() +} + +func (v unknown_v) String() string { + return fmt.Sprintf("%s(unknown):%d", v.t.typeName(), v.v) +} + func unknownType(spec *typeSpec, env *Env) tÿpe { Debug.Printf("Unknown Type: %v", spec) - return typeLiteral{ - name: fmt.Sprintf("unknown:%s", spec.typeName), - readFn: func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r), r.Err() - }, - } + t := unknown_t(spec.typeName) + return &t } // a type error is both an error and a type. It represents a type that we were @@ -62,11 +80,9 @@ func typeError(t string, args ...interface{}) tÿpe { type error_t string +func (e error_t) nü() value { panic("can't create an error val like that") } func (e error_t) typeName() string { return "error" } func (e error_t) Error() string { return string(e) } -func (e error_t) read(r bit.Reader) (value, error) { - return nil, fmt.Errorf("type error: %s", string(e)) -} type typeSpec struct { name string diff --git a/ent/value.go b/ent/value.go index 2b252df..b03be93 100644 --- a/ent/value.go +++ b/ent/value.go @@ -1,3 +1,11 @@ package ent -type value interface{} +import ( + "github.com/jordanorelli/hyperstone/bit" +) + +type value interface { + String() string + tÿpe() tÿpe + read(bit.Reader) error +} diff --git a/ent/vector.go b/ent/vector.go index c8f7e57..37bfc5b 100644 --- a/ent/vector.go +++ b/ent/vector.go @@ -5,8 +5,6 @@ import ( "github.com/jordanorelli/hyperstone/bit" ) -type vector struct{ x, y, z float32 } - func vectorType(spec *typeSpec, env *Env) tÿpe { if spec.encoder != "" { return nil @@ -18,7 +16,7 @@ func vectorType(spec *typeSpec, env *Env) tÿpe { if t == nil { return nil } - return vector_t{elem: t} + return &vector_t{elem: t} } type vector_t struct { @@ -29,19 +27,41 @@ func (t vector_t) typeName() string { return fmt.Sprintf("vector:%s", t.elem.typeName()) } -func (t vector_t) read(r bit.Reader) (value, error) { - var err error - var v interface{} - read := func(f *float32) { - if err != nil { - return +func (t *vector_t) nü() value { + return &vector{t: t} +} + +type vector struct { + t tÿpe + x, y, z value +} + +func (v vector) tÿpe() tÿpe { return v.t } + +func (v *vector) read(r bit.Reader) error { + if v.x == nil { + v.x = v.t.nü() + } + if v.y == nil { + v.y = v.t.nü() + } + if v.z == nil { + v.z = v.t.nü() + } + + type fn func(bit.Reader) error + coalesce := func(fns ...fn) error { + for _, f := range fns { + if err := f(r); err != nil { + return err + } } - v, err = t.elem.read(r) - *f = v.(float32) + return nil } - var out vector - read(&out.x) - read(&out.y) - read(&out.z) - return out, err + + return coalesce(v.x.read, v.y.read, v.z.read) +} + +func (v vector) String() string { + return fmt.Sprintf("vector<%s>{%s %s %s}", v.t.typeName, v.x, v.y, v.z) } From 91f6f235cca06c3bac274bab4fff4698246efbce Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Wed, 28 Sep 2016 21:38:57 -0400 Subject: [PATCH 28/33] PushOneLeftDeltaOneRightNonZero lol these names --- ent/huff.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ent/huff.go b/ent/huff.go index c66d74c..f64ee06 100644 --- a/ent/huff.go +++ b/ent/huff.go @@ -134,7 +134,8 @@ var hlist = nodeList{ r.add(int(bit.ReadUBitVarFP(br)) + 5) }}, lNode{"PushOneLeftDeltaOneRightNonZero", 8, 2942, func(r *selectionReader, br bit.Reader) { - panic("not implemented: PushOneLeftDeltaOneRightNonZero") + r.add(1) + r.push(int(bit.ReadUBitVarFP(br))) }}, lNode{"PopAllButOnePlusOne", 29, 1837, func(r *selectionReader, br bit.Reader) { r.pop(-1) From 75c3ac466b52f3de29eb20006867fe74cdc90ca3 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Thu, 29 Sep 2016 11:18:52 -0400 Subject: [PATCH 29/33] we got a mem overflow now --- ent/array.go | 5 ++++- ent/atoms.go | 2 +- ent/class.go | 12 ++---------- ent/entity.go | 23 +++++++++-------------- ent/env.go | 16 +++++++++++++--- ent/generic.go | 9 ++++++--- ent/handle.go | 2 +- ent/huff.go | 3 ++- ent/selection.go | 4 ++-- ent/vector.go | 2 +- 10 files changed, 41 insertions(+), 37 deletions(-) diff --git a/ent/array.go b/ent/array.go index 9e332cb..1d6684a 100644 --- a/ent/array.go +++ b/ent/array.go @@ -72,5 +72,8 @@ func (a array) read(r bit.Reader) error { } func (a array) String() string { - return fmt.Sprintf("%s%v", a.t.typeName(), a.slots) + if len(a.slots) > 8 { + return fmt.Sprintf("%s(%d)%v...", a.t.typeName(), len(a.slots), a.slots[:8]) + } + return fmt.Sprintf("%s(%d)%v", a.t.typeName(), len(a.slots), a.slots) } diff --git a/ent/atoms.go b/ent/atoms.go index 2eaf83f..c5ec09e 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -111,7 +111,7 @@ func (v *uint32_v) read(r bit.Reader) error { } func (v uint32_v) String() string { - return strconv.FormatUint(uint64(v), 0) + return strconv.FormatUint(uint64(v), 10) } // ------------------------------------------------------------------------------ diff --git a/ent/class.go b/ent/class.go index 6ac14fc..201ccf9 100644 --- a/ent/class.go +++ b/ent/class.go @@ -2,7 +2,6 @@ package ent import ( "fmt" - "github.com/jordanorelli/hyperstone/bit" ) type class struct { @@ -11,17 +10,10 @@ type class struct { fields []field } -func (c class) String() string { - return fmt.Sprintf("<%s.%d>", c.name, c.version) -} +func (c class) String() string { return c.typeName() } func (c class) typeName() string { - return fmt.Sprintf("class:%s", c.name) -} - -func (c *class) read(r bit.Reader) (value, error) { - bit.ReadBool(r) // ??? - return c.nü(), nil + return fmt.Sprintf("%s.%d", c.name, c.version) } func (c *class) nü() value { diff --git a/ent/entity.go b/ent/entity.go index d93ea20..a4142dc 100644 --- a/ent/entity.go +++ b/ent/entity.go @@ -11,17 +11,7 @@ type entity struct { } func (e *entity) read(r bit.Reader) error { - Debug.Printf("entity %s read", e.className()) - sr := new(selectionReader) - selections, err := sr.readSelections(r, htree) - if err != nil { - return wrap(err, "entity of type %s failed to read selections", e.className()) - } - for _, s := range selections { - if err := s.fillSlots(e, r); err != nil { - return err - } - } + bit.ReadBool(r) // ??? return nil } @@ -33,11 +23,16 @@ func (e *entity) className() string { } func (e *entity) String() string { - return fmt.Sprintf("%s{%v}", e.class.typeName(), e.slots) + return fmt.Sprintf("%s{id: ?}", e.class.typeName()) } -func (e *entity) tÿpe() tÿpe { return e.class } -func (e *entity) slotType(i int) tÿpe { return e.class.fields[i].tÿpe } +func (e *entity) tÿpe() tÿpe { return e.class } +func (e *entity) slotType(i int) tÿpe { + if i >= len(e.class.fields) { + return typeError("index out of range in slotType: %d is beyond capacity %d", i, len(e.class.fields)) + } + return e.class.fields[i].tÿpe +} func (e *entity) slotName(i int) string { return e.class.fields[i].name } func (e *entity) setSlotValue(i int, v value) { e.slots[i] = v } func (e *entity) getSlotValue(i int) value { return e.slots[i] } diff --git a/ent/env.go b/ent/env.go index 180fd30..676120b 100644 --- a/ent/env.go +++ b/ent/env.go @@ -113,6 +113,7 @@ func (e *Env) fillClasses(flat *dota.CSVCMsg_FlattenedSerializer) { class.fields = make([]field, len(s.GetFieldsIndex())) for i, id := range s.GetFieldsIndex() { + Debug.Printf("class %s has field %s (%s)", name, e.fields[id].name, e.fields[id].typeName()) class.fields[i] = e.fields[id] } } @@ -149,6 +150,7 @@ func (e *Env) syncBaselineTable(t *stbl.Table) { } r := new(bit.BufReader) + sr := new(selectionReader) for _, entry := range t.Entries() { netId, err := strconv.Atoi(entry.Key) if err != nil { @@ -166,10 +168,18 @@ func (e *Env) syncBaselineTable(t *stbl.Table) { continue } Debug.Printf("syncBaselines key: %s className: %s", entry.Key, c.name) - ent := c.nü() + ent := c.nü().(*entity) r.SetSource(entry.Value) - if err := ent.read(r); err != nil { - Debug.Printf("syncBaselines failed to fill an entity: %v", err) + selections, err := sr.readSelections(r) + if err != nil { + Debug.Printf("unable to read selections for %s: %v", className, err) + continue + } + Debug.Printf("selections: %v", selections) + for _, s := range selections { + if err := s.fillSlots(ent, r); err != nil { + Debug.Printf("unable to fill slots for %s: %v", className, err) + } } } } diff --git a/ent/generic.go b/ent/generic.go index 9a44ff0..b1aa170 100644 --- a/ent/generic.go +++ b/ent/generic.go @@ -24,7 +24,7 @@ func genericType(spec *typeSpec, env *Env) tÿpe { switch genericName { case "CHandle", "CStrongHandle": - t := handle_t(spec.typeName) + t := handle_t(fmt.Sprintf("%s<%s>", genericName, spec.typeName)) return &t case "CUtlVector": return &cutl_vector_t{elem} @@ -76,7 +76,7 @@ func (t *cutl_vector_t) nü() value { } func (t cutl_vector_t) typeName() string { - return fmt.Sprintf("vector:%s", t.elem.typeName()) + return fmt.Sprintf("CUtlVector<%s>", t.elem.typeName()) } type cutl_vector struct { @@ -93,5 +93,8 @@ func (v *cutl_vector) read(r bit.Reader) error { } func (v cutl_vector) String() string { - return fmt.Sprintf("%s<%v>", v.t.typeName(), v.slots) + if len(v.slots) > 8 { + return fmt.Sprintf("%s(%d)%v...", v.t.typeName(), len(v.slots), v.slots[:8]) + } + return fmt.Sprintf("%s(%d)%v", v.t.typeName(), len(v.slots), v.slots) } diff --git a/ent/handle.go b/ent/handle.go index 3ea0f10..167a53a 100644 --- a/ent/handle.go +++ b/ent/handle.go @@ -25,7 +25,7 @@ func (h *handle) read(r bit.Reader) error { } func (h handle) String() string { - return fmt.Sprintf("handle<%s>: %d", h.t.typeName(), h.id) + return fmt.Sprintf("%s:%d", h.t.typeName(), h.id) } func handleType(spec *typeSpec, env *Env) tÿpe { diff --git a/ent/huff.go b/ent/huff.go index f64ee06..6d24ab6 100644 --- a/ent/huff.go +++ b/ent/huff.go @@ -134,6 +134,7 @@ var hlist = nodeList{ r.add(int(bit.ReadUBitVarFP(br)) + 5) }}, lNode{"PushOneLeftDeltaOneRightNonZero", 8, 2942, func(r *selectionReader, br bit.Reader) { + Debug.Printf("PushOneLeftDeltaOneRightNonZero") r.add(1) r.push(int(bit.ReadUBitVarFP(br))) }}, @@ -267,4 +268,4 @@ var hlist = nodeList{ }}, } -var htree = makeTree(hlist) +var huffRoot = makeTree(hlist) diff --git a/ent/selection.go b/ent/selection.go index 7f77eb6..594aa68 100644 --- a/ent/selection.go +++ b/ent/selection.go @@ -67,11 +67,11 @@ type selectionReader struct { all [1024]selection } -func (r *selectionReader) readSelections(br bit.Reader, n node) ([]selection, error) { +func (r *selectionReader) readSelections(br bit.Reader) ([]selection, error) { r.cur.count = 1 r.cur.vals[0] = -1 r.count = 0 - for fn := walk(n, br); fn != nil; fn = walk(n, br) { + for fn := walk(huffRoot, br); fn != nil; fn = walk(huffRoot, br) { if err := br.Err(); err != nil { return nil, fmt.Errorf("unable to read selection: bit reader error: %v", err) } diff --git a/ent/vector.go b/ent/vector.go index 37bfc5b..52b8964 100644 --- a/ent/vector.go +++ b/ent/vector.go @@ -24,7 +24,7 @@ type vector_t struct { } func (t vector_t) typeName() string { - return fmt.Sprintf("vector:%s", t.elem.typeName()) + return fmt.Sprintf("vector<%s>", t.elem.typeName()) } func (t *vector_t) nü() value { From d7a9bd98bf6b96d2a67b20b05898726678d0b650 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Thu, 29 Sep 2016 11:39:31 -0400 Subject: [PATCH 30/33] array is slotted --- ent/array.go | 8 ++++++++ ent/class.go | 2 +- ent/env.go | 2 +- ent/generic.go | 2 +- ent/selection.go | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ent/array.go b/ent/array.go index 1d6684a..b22640b 100644 --- a/ent/array.go +++ b/ent/array.go @@ -77,3 +77,11 @@ func (a array) String() string { } return fmt.Sprintf("%s(%d)%v", a.t.typeName(), len(a.slots), a.slots) } + +func (a array) slotType(int) tÿpe { return a.t.elem } +func (a array) slotName(n int) string { return strconv.Itoa(n) } +func (a array) setSlotValue(slot int, v value) { + // TODO: type check here? + a.slots[slot] = v +} +func (a array) getSlotValue(slot int) value { return a.slots[slot] } diff --git a/ent/class.go b/ent/class.go index 201ccf9..0237318 100644 --- a/ent/class.go +++ b/ent/class.go @@ -13,7 +13,7 @@ type class struct { func (c class) String() string { return c.typeName() } func (c class) typeName() string { - return fmt.Sprintf("%s.%d", c.name, c.version) + return fmt.Sprintf("%s_v%d", c.name, c.version) } func (c *class) nü() value { diff --git a/ent/env.go b/ent/env.go index 676120b..3b060e8 100644 --- a/ent/env.go +++ b/ent/env.go @@ -178,7 +178,7 @@ func (e *Env) syncBaselineTable(t *stbl.Table) { Debug.Printf("selections: %v", selections) for _, s := range selections { if err := s.fillSlots(ent, r); err != nil { - Debug.Printf("unable to fill slots for %s: %v", className, err) + Debug.Printf("unable to fill selection %s for %s: %v", s, className, err) } } } diff --git a/ent/generic.go b/ent/generic.go index b1aa170..5a3a5e5 100644 --- a/ent/generic.go +++ b/ent/generic.go @@ -24,7 +24,7 @@ func genericType(spec *typeSpec, env *Env) tÿpe { switch genericName { case "CHandle", "CStrongHandle": - t := handle_t(fmt.Sprintf("%s<%s>", genericName, spec.typeName)) + t := handle_t(fmt.Sprintf("%s<%s>", genericName, elem.typeName())) return &t case "CUtlVector": return &cutl_vector_t{elem} diff --git a/ent/selection.go b/ent/selection.go index 594aa68..16c9f74 100644 --- a/ent/selection.go +++ b/ent/selection.go @@ -46,7 +46,7 @@ func (s selection) fillSlotsIter(offset int, dest slotted, path string, r bit.Re } vs, ok := v.(slotted) if !ok { - return fmt.Errorf("dest isn't slotted") + return fmt.Errorf("dest %s (%s) isn't slotted", dest.slotName(slot), dest.slotType(slot).typeName()) } return s.fillSlotsIter(offset+1, vs, fmt.Sprintf("%s.%s", path, dest.slotName(slot)), r) } From 701d08ec7c2701338ef2bc438378c22adbfb74ee Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Thu, 29 Sep 2016 16:03:03 -0400 Subject: [PATCH 31/33] fix some overflows --- bit/decode.go | 20 ++++++++++++++++++++ ent/array.go | 2 +- ent/atoms.go | 12 ++---------- ent/env.go | 4 +++- ent/generic.go | 14 +++++++++++++- ent/selection.go | 2 +- 6 files changed, 40 insertions(+), 14 deletions(-) diff --git a/bit/decode.go b/bit/decode.go index 41c6d0f..6edc6b6 100644 --- a/bit/decode.go +++ b/bit/decode.go @@ -99,6 +99,26 @@ func ReadVarInt32(r Reader) uint32 { return uint32(x) } +// reads a 32bit varint +func ReadVarInt16(r Reader) uint16 { + var ( + x uint64 + b uint64 + shift uint + ) + for ; shift < 16; shift += 7 { + b = r.ReadBits(8) + if r.Err() != nil { + return 0 + } + x |= b & 0x7f << shift + if b&0x80 == 0 { + return uint16(x) + } + } + return uint16(x) +} + func ReadBool(r Reader) bool { return r.ReadBits(1) != 0 } diff --git a/ent/array.go b/ent/array.go index b22640b..306f016 100644 --- a/ent/array.go +++ b/ent/array.go @@ -50,7 +50,7 @@ type array_t struct { } func (t *array_t) nü() value { return array{t: t, slots: make([]value, t.count)} } -func (t array_t) typeName() string { return fmt.Sprintf("array:%s", t.elem.typeName()) } +func (t array_t) typeName() string { return fmt.Sprintf("%s[%d]", t.elem.typeName(), t.count) } type array struct { t *array_t diff --git a/ent/atoms.go b/ent/atoms.go index c5ec09e..71c5fd2 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -48,11 +48,7 @@ type uint8_v uint8 func (v uint8_v) tÿpe() tÿpe { return uint8_t } func (v *uint8_v) read(r bit.Reader) error { - u := bit.ReadVarInt(r) - if u > 1<<8-1 { - return fmt.Errorf("uint8 overflow: %d", u) - } - *v = uint8_v(u) + *v = uint8_v(r.ReadBits(8)) return r.Err() } @@ -75,11 +71,7 @@ type uint16_v uint16 func (v uint16_v) tÿpe() tÿpe { return uint16_t } func (v *uint16_v) read(r bit.Reader) error { - u := bit.ReadVarInt(r) - if u > 1<<16-1 { - return fmt.Errorf("uint16 overflow: %d", u) - } - *v = uint16_v(u) + *v = uint16_v(bit.ReadVarInt16(r)) return r.Err() } diff --git a/ent/env.go b/ent/env.go index 3b060e8..ca9ab38 100644 --- a/ent/env.go +++ b/ent/env.go @@ -2,6 +2,7 @@ package ent import ( "github.com/golang/protobuf/proto" + "os" "strconv" "github.com/jordanorelli/hyperstone/bit" @@ -178,7 +179,8 @@ func (e *Env) syncBaselineTable(t *stbl.Table) { Debug.Printf("selections: %v", selections) for _, s := range selections { if err := s.fillSlots(ent, r); err != nil { - Debug.Printf("unable to fill selection %s for %s: %v", s, className, err) + Debug.Printf("syncBaseline fill error: %v", err) + os.Exit(1) } } } diff --git a/ent/generic.go b/ent/generic.go index 5a3a5e5..ea80720 100644 --- a/ent/generic.go +++ b/ent/generic.go @@ -2,6 +2,7 @@ package ent import ( "fmt" + "strconv" "strings" "github.com/jordanorelli/hyperstone/bit" @@ -80,7 +81,7 @@ func (t cutl_vector_t) typeName() string { } type cutl_vector struct { - t tÿpe + t *cutl_vector_t slots []value } @@ -98,3 +99,14 @@ func (v cutl_vector) String() string { } return fmt.Sprintf("%s(%d)%v", v.t.typeName(), len(v.slots), v.slots) } + +func (v *cutl_vector) slotType(int) tÿpe { return v.t.elem } +func (v *cutl_vector) slotName(n int) string { return strconv.Itoa(n) } +func (v *cutl_vector) setSlotValue(slot int, val value) { + if slot == len(v.slots) { + v.slots = append(v.slots, val) + } else { + v.slots[slot] = val + } +} +func (v *cutl_vector) getSlotValue(slot int) value { return v.slots[slot] } diff --git a/ent/selection.go b/ent/selection.go index 16c9f74..0f5f620 100644 --- a/ent/selection.go +++ b/ent/selection.go @@ -32,7 +32,7 @@ func (s selection) fillSlotsIter(offset int, dest slotted, path string, r bit.Re case 1: v := dest.slotType(slot).nü() if err := v.read(r); err != nil { - return fmt.Errorf("unable to fill selection: %v", err) + return fmt.Errorf("unable to fill selection %v for %s.%s (%s): %v", s, path, dest.slotName(slot), dest.slotType(slot).typeName(), err) } old := dest.getSlotValue(slot) dest.setSlotValue(slot, v) From 96cf3791921b0f0cd8b4e9ed66378732ca3d0bf9 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sat, 1 Oct 2016 15:15:14 -0400 Subject: [PATCH 32/33] uint64 fixed is a thing --- bit/decode.go | 64 ++++++++--------------- ent/array.go | 20 +++++-- ent/atoms.go | 132 +++++++++++++++++++++++++++++++++++++++++++---- ent/env.go | 3 ++ ent/generic.go | 3 +- ent/selection.go | 3 +- 6 files changed, 167 insertions(+), 58 deletions(-) diff --git a/bit/decode.go b/bit/decode.go index 6edc6b6..4478c2c 100644 --- a/bit/decode.go +++ b/bit/decode.go @@ -61,62 +61,42 @@ func ReadUBitVarFP(r Reader) uint64 { // in little-endian order. The most-significant bit of each byte represents a // continuation bit. func ReadVarInt(r Reader) uint64 { - var ( - x uint64 - b uint64 - shift uint - ) - for ; shift < 64; shift += 7 { - b = r.ReadBits(8) + var x uint64 + var s uint + for i := 0; ; i++ { + b := r.ReadBits(8) if r.Err() != nil { return 0 } - x |= b & 0x7f << shift - if b&0x80 == 0 { - return x + if b < 0x80 { + if i > 9 || i == 9 && b > 1 { + panic("varint overflow") + } + return x | b< 4 || i == 4 && b > 0xf { + panic("varint32 overflow") + } + return x | uint32(b)< 1<<32-1 { - return fmt.Errorf("uint32 overflow: %d", u) - } - *v = uint32_v(u) + *v = uint32_v(bit.ReadVarInt32(r)) return r.Err() } @@ -121,7 +117,7 @@ type uint64_v uint64 func (v uint64_v) tÿpe() tÿpe { return uint64_t } func (v *uint64_v) read(r bit.Reader) error { - *v = uint64_v(bit.ReadVarInt(r)) + *v = uint64_v(bit.ReadVarInt32(r)) return r.Err() } @@ -129,6 +125,31 @@ func (v uint64_v) String() string { return strconv.FormatUint(uint64(v), 10) } +// ------------------------------------------------------------------------------ +// uint64fixed +// +// (a uint64 value that is always represented on the wire with 64 bits) +// ------------------------------------------------------------------------------ + +var uint64fixed_t = &typeLiteral{ + name: "uint64fixed", + newFn: func() value { + return new(uint64fixed_v) + }, +} + +type uint64fixed_v uint64 + +func (v uint64fixed_v) tÿpe() tÿpe { return uint64fixed_t } +func (v *uint64fixed_v) read(r bit.Reader) error { + *v = uint64fixed_v(r.ReadBits(64)) + return r.Err() +} + +func (v uint64fixed_v) String() string { + return strconv.FormatUint(uint64(v), 10) +} + // ------------------------------------------------------------------------------ // int8 // ------------------------------------------------------------------------------ @@ -153,6 +174,30 @@ func (v int8_v) String() string { return strconv.FormatInt(int64(v), 10) } +// ------------------------------------------------------------------------------ +// int16 +// ------------------------------------------------------------------------------ + +var int16_t = &typeLiteral{ + name: "int16", + newFn: func() value { + return new(int16_v) + }, +} + +type int16_v int16 + +func (v int16_v) tÿpe() tÿpe { return int16_t } +func (v *int16_v) read(r bit.Reader) error { + // TODO: bounds check here? + *v = int16_v(bit.ReadZigZag32(r)) + return r.Err() +} + +func (v int16_v) String() string { + return strconv.FormatInt(int64(v), 10) +} + // ------------------------------------------------------------------------------ // int32 // ------------------------------------------------------------------------------ @@ -176,6 +221,29 @@ func (v int32_v) String() string { return strconv.FormatInt(int64(v), 10) } +// ------------------------------------------------------------------------------ +// int64 +// ------------------------------------------------------------------------------ + +var int64_t = &typeLiteral{ + name: "int64", + newFn: func() value { + return new(int64_v) + }, +} + +type int64_v int64 + +func (v int64_v) tÿpe() tÿpe { return int64_t } +func (v *int64_v) read(r bit.Reader) error { + *v = int64_v(bit.ReadZigZag(r)) + return r.Err() +} + +func (v int64_v) String() string { + return strconv.FormatInt(int64(v), 10) +} + // ------------------------------------------------------------------------------ // CUtlStringToken // @@ -229,24 +297,70 @@ func (c color) String() string { return fmt.Sprintf("#%x%x%x%x", c.r, c.g, c.b, c.a) } +// ------------------------------------------------------------------------------ +// CUtlSymbolLarge +// ------------------------------------------------------------------------------ + +var cutl_string_t = typeLiteral{ + name: "CUtlSymbolLarge", + newFn: func() value { + return new(cutl_string_v) + }, +} + +type cutl_string_v string + +func (v cutl_string_v) tÿpe() tÿpe { return cutl_string_t } +func (v cutl_string_v) String() string { return string(v) } +func (v *cutl_string_v) read(r bit.Reader) error { + *v = cutl_string_v(bit.ReadString(r)) + return r.Err() +} + var atom_types = []tÿpe{ bool_t, uint8_t, uint16_t, uint32_t, - uint64_t, int8_t, + int16_t, int32_t, + int64_t, stringToken_t, color_t, + cutl_string_t, } func atomType(spec *typeSpec, env *Env) tÿpe { for _, t := range atom_types { if t.typeName() == spec.typeName { Debug.Printf(" atom type: %s", t.typeName()) + if spec.bits != 0 { + return typeError("spec can't be atom type: has bit specification: %v", spec) + } + if spec.encoder != "" { + return typeError("spec can't be atom type: has encoder specification: %v", spec) + } + if spec.flags != 0 { + return typeError("spec can't be atom type: has flags: %v", spec) + } + if spec.high != 0 { + return typeError("spec can't be atom type: has high value constraint: %v", spec) + } + if spec.low != 0 { + return typeError("spec can't be atom type: has low value constraint: %v", spec) + } + if spec.serializer != "" { + return typeError("spec can't be atom type: has serializer: %v", spec) + } return t } } + if spec.typeName == "uint64" { + if spec.encoder == "fixed64" { + return uint64fixed_t + } + return uint64_t + } return nil } diff --git a/ent/env.go b/ent/env.go index ca9ab38..37ea8bd 100644 --- a/ent/env.go +++ b/ent/env.go @@ -63,6 +63,9 @@ func (e *Env) mergeSendTables(m *dota.CDemoSendTables) error { if err != nil { return wrap(err, "unable to get serializers in sendtables") } + for i, s := range flat.GetSymbols() { + Debug.Printf("symbol %d: %s", i, s) + } e.symbols = symbolTable(flat.GetSymbols()) e.stubClasses(flat) if err := e.parseFields(flat); err != nil { diff --git a/ent/generic.go b/ent/generic.go index ea80720..c7edfe8 100644 --- a/ent/generic.go +++ b/ent/generic.go @@ -42,10 +42,10 @@ func genericName(name string) ([2]string, error) { for i, r := range runes { if r == '<' { if depth == 0 { + b_start = i out[0] = strings.TrimSpace(string(runes[0:i])) } depth++ - b_start = i } if r == '>' { depth-- @@ -89,6 +89,7 @@ func (v *cutl_vector) tÿpe() tÿpe { return v.t } func (v *cutl_vector) read(r bit.Reader) error { count := bit.ReadVarInt32(r) + Debug.Printf("allocating cutl_vector of size %d with element type %s", count, v.t.elem.typeName()) v.slots = make([]value, count) return r.Err() } diff --git a/ent/selection.go b/ent/selection.go index 0f5f620..c868d5d 100644 --- a/ent/selection.go +++ b/ent/selection.go @@ -30,11 +30,12 @@ func (s selection) fillSlotsIter(offset int, dest slotted, path string, r bit.Re } switch s.count - offset { case 1: + old := dest.getSlotValue(slot) v := dest.slotType(slot).nü() + Debug.Printf("%v %s.%s (%s) %v read", s, path, dest.slotName(slot), dest.slotType(slot).typeName(), old) if err := v.read(r); err != nil { return fmt.Errorf("unable to fill selection %v for %s.%s (%s): %v", s, path, dest.slotName(slot), dest.slotType(slot).typeName(), err) } - old := dest.getSlotValue(slot) dest.setSlotValue(slot, v) Debug.Printf("%v %s.%s (%s) %v -> %v", s, path, dest.slotName(slot), dest.slotType(slot).typeName(), old, v) return nil From 75217517329416a502349a4f45a4bd677192cf03 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Tue, 4 Oct 2016 11:34:22 -0400 Subject: [PATCH 33/33] =?UTF-8?q?=C2=AF\=5F(=E3=83=84)=5F/=C2=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bit/decode.go | 26 +++++++++--------------- bit/misc.go | 9 +++++++++ ent/array.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ ent/atoms.go | 24 ++++++++++++++++++++++ ent/env.go | 16 +++++++-------- ent/generic.go | 12 +++++++---- ent/huff_test.go | 2 +- 7 files changed, 111 insertions(+), 30 deletions(-) create mode 100644 bit/misc.go diff --git a/bit/decode.go b/bit/decode.go index 4478c2c..5f4c883 100644 --- a/bit/decode.go +++ b/bit/decode.go @@ -62,41 +62,33 @@ func ReadUBitVarFP(r Reader) uint64 { // continuation bit. func ReadVarInt(r Reader) uint64 { var x uint64 - var s uint - for i := 0; ; i++ { + for s := uint64(0); s < 70; s += 7 { b := r.ReadBits(8) if r.Err() != nil { return 0 } + x |= b & 0x7f << s if b < 0x80 { - if i > 9 || i == 9 && b > 1 { - panic("varint overflow") - } - return x | b< 4 || i == 4 && b > 0xf { - panic("varint32 overflow") - } - return x | uint32(b)<= len(s.valid) { + s.valid = s.buf[:slot+1] + } +} +func (s *string_v) getSlotValue(slot int) value { + v := char_v(s.buf[slot]) + return &v +} diff --git a/ent/atoms.go b/ent/atoms.go index bac38be..935a985 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -317,8 +317,32 @@ func (v *cutl_string_v) read(r bit.Reader) error { return r.Err() } +// ------------------------------------------------------------------------------ +// char +// +// this seems absolutely wrong, but it's how it's done in clarity. a char is a +// cstring? wtf? +// ------------------------------------------------------------------------------ + +var char_t = typeLiteral{ + name: "char", + newFn: func() value { + return new(char_v) + }, +} + +type char_v byte + +func (v char_v) tÿpe() tÿpe { return char_t } +func (v char_v) String() string { return string(v) } +func (v *char_v) read(r bit.Reader) error { + *v = char_v(r.ReadByte()) + return r.Err() +} + var atom_types = []tÿpe{ bool_t, + char_t, uint8_t, uint16_t, uint32_t, diff --git a/ent/env.go b/ent/env.go index 37ea8bd..ec8ebcc 100644 --- a/ent/env.go +++ b/ent/env.go @@ -2,7 +2,6 @@ package ent import ( "github.com/golang/protobuf/proto" - "os" "strconv" "github.com/jordanorelli/hyperstone/bit" @@ -11,12 +10,13 @@ import ( ) type Env struct { - symbols symbolTable - source bit.BufReader - classes map[string]*classHistory - netIds map[int]string - fields []field - strings *stbl.Dict + symbols symbolTable + source bit.BufReader + classes map[string]*classHistory + netIds map[int]string + fields []field + strings *stbl.Dict + entities map[int]*entity } func NewEnv() *Env { @@ -183,7 +183,7 @@ func (e *Env) syncBaselineTable(t *stbl.Table) { for _, s := range selections { if err := s.fillSlots(ent, r); err != nil { Debug.Printf("syncBaseline fill error: %v", err) - os.Exit(1) + break } } } diff --git a/ent/generic.go b/ent/generic.go index c7edfe8..c98528d 100644 --- a/ent/generic.go +++ b/ent/generic.go @@ -88,9 +88,7 @@ type cutl_vector struct { func (v *cutl_vector) tÿpe() tÿpe { return v.t } func (v *cutl_vector) read(r bit.Reader) error { - count := bit.ReadVarInt32(r) - Debug.Printf("allocating cutl_vector of size %d with element type %s", count, v.t.elem.typeName()) - v.slots = make([]value, count) + bit.ReadVarInt32(r) // ?? return r.Err() } @@ -110,4 +108,10 @@ func (v *cutl_vector) setSlotValue(slot int, val value) { v.slots[slot] = val } } -func (v *cutl_vector) getSlotValue(slot int) value { return v.slots[slot] } + +func (v *cutl_vector) getSlotValue(slot int) value { + if slot >= len(v.slots) { + return nil + } + return v.slots[slot] +} diff --git a/ent/huff_test.go b/ent/huff_test.go index e40ce5d..d85af9f 100644 --- a/ent/huff_test.go +++ b/ent/huff_test.go @@ -66,5 +66,5 @@ func TestTree(t *testing.T) { } } - testWalk(htree, "") + testWalk(huffRoot, "") }