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))