Merge pull request #1 from jordanorelli/types

Types
master
Jordan Orelli 8 years ago committed by GitHub
commit 53e06884b2

@ -61,19 +61,15 @@ 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
for s := uint64(0); s < 70; s += 7 {
b := r.ReadBits(8)
if r.Err() != nil {
return 0
}
x |= b & 0x7f << shift
if b&0x80 == 0 {
return x
x |= b & 0x7f << s
if b < 0x80 {
break
}
}
return x
@ -81,22 +77,18 @@ func ReadVarInt(r Reader) uint64 {
// reads a 32bit varint
func ReadVarInt32(r Reader) uint32 {
var (
x uint64
b uint64
shift uint
)
for ; shift < 32; shift += 7 {
b = r.ReadBits(8)
var x uint32
for s := uint32(0); s < 35; s += 7 {
b := uint32(r.ReadBits(8))
if r.Err() != nil {
return 0
}
x |= b & 0x7f << shift
if b&0x80 == 0 {
return uint32(x)
x |= b & 0x7f << s
if b < 0x80 {
break
}
}
return uint32(x)
return x
}
func ReadBool(r Reader) bool {

@ -0,0 +1,9 @@
package bit
import (
"math"
)
func Length(n uint) uint {
return uint(math.Floor(math.Log2(float64(n)) + 1))
}

@ -1,17 +1,149 @@
package ent
import (
"fmt"
"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)
if elemName == "char" {
return string_t(count)
}
return &array_t{elem: elemType, count: count}
}
func parseArrayName(s string) (string, uint) {
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])), uint(n)
}
}
panic("invalid array type name: " + s)
}
type array_t struct {
elem tÿpe
count uint
bits uint
}
func (t *array_t) sizeBits() uint {
if t.bits == 0 {
t.bits = bit.Length(t.count)
}
return t.bits
}
func (t *array_t) nü() value { return array{t: t, slots: make([]value, t.count)} }
func (t array_t) typeName() string { return fmt.Sprintf("%s[%d]", t.elem.typeName(), t.count) }
type array struct {
slots []interface{}
_slotType string
decoder
t *array_t
slots []value
}
func (a array) tÿpe() tÿpe { return a.t }
func (a array) read(r bit.Reader) error {
n := r.ReadBits(a.t.bits)
Debug.Printf("reading %d array elements", n)
for i := uint64(0); i < n; i++ {
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 r.Err()
}
func (a array) String() string {
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)
}
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] }
// ------------------------------------------------------------------------------
// strings are a special case of arrays
// ------------------------------------------------------------------------------
type string_t int
func (t string_t) nü() value {
return &string_v{t: t, buf: make([]byte, int(t))}
}
func (t string_t) typeName() string { return "string" }
type string_v struct {
t string_t
buf []byte // the buffer of all possible bytes
valid []byte // selection of current valid bytes within buf
}
func (s *string_v) tÿpe() tÿpe { return s.t }
func (s *string_v) read(r bit.Reader) error {
for i := 0; i < int(s.t); i++ {
b := r.ReadBits(8)
if b == 0 {
s.valid = s.buf[:i]
return r.Err()
}
s.buf[i] = byte(b & 0xff)
}
s.valid = s.buf
return r.Err()
}
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 }
func (s *string_v) String() string {
return string(s.valid)
}
func (s *string_v) slotType(int) tÿpe { return char_t }
func (s *string_v) slotName(n int) string { return strconv.Itoa(n) }
func (s *string_v) setSlotValue(slot int, v value) {
s.buf[slot] = byte(*v.(*char_v))
if slot >= 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
}

@ -0,0 +1,390 @@
package ent
import (
"fmt"
"github.com/jordanorelli/hyperstone/bit"
"strconv"
)
// ------------------------------------------------------------------------------
// bool
// ------------------------------------------------------------------------------
var bool_t = &typeLiteral{
name: "bool",
newFn: func() value {
return new(bool_v)
},
}
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)
},
}
type uint8_v uint8
func (v uint8_v) tÿpe() tÿpe { return uint8_t }
func (v *uint8_v) read(r bit.Reader) error {
*v = uint8_v(bit.ReadVarInt32(r))
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)
},
}
type uint16_v uint16
func (v uint16_v) tÿpe() tÿpe { return uint16_t }
func (v *uint16_v) read(r bit.Reader) error {
*v = uint16_v(bit.ReadVarInt32(r))
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)
},
}
type uint32_v uint32
func (v uint32_v) tÿpe() tÿpe { return uint32_t }
func (v *uint32_v) read(r bit.Reader) error {
*v = uint32_v(bit.ReadVarInt32(r))
return r.Err()
}
func (v uint32_v) String() string {
return strconv.FormatUint(uint64(v), 10)
}
// ------------------------------------------------------------------------------
// uint64
// ------------------------------------------------------------------------------
var uint64_t = &typeLiteral{
name: "uint64",
newFn: func() value {
return new(uint64_v)
},
}
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.ReadVarInt32(r))
return r.Err()
}
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
// ------------------------------------------------------------------------------
var int8_t = &typeLiteral{
name: "int8",
newFn: func() value {
return new(int8_v)
},
}
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)
}
// ------------------------------------------------------------------------------
// 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
// ------------------------------------------------------------------------------
var int32_t = &typeLiteral{
name: "int32",
newFn: func() value {
return new(int32_v)
},
}
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)
}
// ------------------------------------------------------------------------------
// 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
//
// 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)
},
}
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)
}
// ------------------------------------------------------------------------------
// 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()
}
// ------------------------------------------------------------------------------
// 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,
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
}

@ -2,53 +2,57 @@ 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
type class struct {
name string
version int
fields []field
}
// all other entities for this class use this instance as a prototype
baseline *Entity
func (c class) String() string { return c.typeName() }
// 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) typeName() string {
return fmt.Sprintf("%s_v%d", c.name, c.version)
}
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) nü() value {
return &entity{class: c, slots: make([]value, len(c.fields))}
}
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
type classHistory struct {
versions map[int]*class
oldest *class
newest *class
}
func (c Class) String() string {
return fmt.Sprintf("{%s %d}", c.Name, c.Version)
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
}
// A class is identified by the union of its name and version.
type classId struct {
name Symbol
version int
func (h *classHistory) version(v int) *class {
if h.versions == nil {
return nil
}
return h.versions[v]
}
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]
func classType(spec *typeSpec, env *Env) tÿpe {
if spec.serializer != "" {
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
}

@ -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
}

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

@ -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
}

@ -1,23 +1,38 @@
package ent
type Entity struct {
*Class
serial int
slots []interface{}
isBaseline bool
import (
"fmt"
"github.com/jordanorelli/hyperstone/bit"
)
type entity struct {
class *class
slots []value
}
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
func (e *entity) read(r bit.Reader) error {
bit.ReadBool(r) // ???
return nil
}
func (e *entity) className() string {
if e.class != nil {
return e.class.name
}
if !e.isBaseline && e.Class.baseline != nil {
return e.Class.baseline.slotValue(n)
return "<None>"
}
func (e *entity) String() string {
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 {
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 nil
return e.class.fields[i].tÿpe
}
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 }
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] }

@ -0,0 +1,203 @@
package ent
import (
"github.com/golang/protobuf/proto"
"strconv"
"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
entities map[int]*entity
}
func NewEnv() *Env {
e := &Env{
classes: make(map[string]*classHistory),
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:
if err := e.mergeSendTables(v); err != nil {
return err
}
case *dota.CDemoClassInfo:
e.mergeClassInfo(v)
e.syncBaseline()
}
return nil
}
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")
flat, err := getSerializers(m)
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 {
return wrap(err, "unable to parse serializer fields")
}
e.fillClasses(flat)
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()
for _, s := range serializers {
name := e.symbol(int(s.GetSerializerNameSym()))
v := int(s.GetSerializerVersion())
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
}
h.add(c)
}
}
// 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() {
f := &e.fields[i]
if err := f.fromProto(ff, e); err != nil {
return err
}
}
return nil
}
// 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].version(v)
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]
}
}
}
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.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)
sr := new(selectionReader)
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ü().(*entity)
r.SetSource(entry.Value)
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("syncBaseline fill error: %v", err)
break
}
}
}
}
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)
}

@ -1,109 +1,27 @@
package ent
import (
"bytes"
"fmt"
"github.com/jordanorelli/hyperstone/dota"
)
/*
type Field struct {
Name string
Type
type field struct {
name string
tÿpe
}
*/
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)
func (f *field) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) error {
Debug.Printf("parse flat field: %s", prettyFlatField(flat, env))
t := parseFieldType(flat, env)
if t == nil {
return fmt.Errorf("unable to parse type %s", prettyFlatField(flat, env))
}
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()))
if err, ok := t.(error); ok {
return wrap(err, "unable to parse type %s", prettyFlatField(flat, env))
}
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)
Debug.Printf(" type: %v", t)
f.tÿpe = t
f.name = env.symbol(int(flat.GetVarNameSym()))
return nil
}
// 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 }

@ -1,11 +1,10 @@
package ent
import (
"fmt"
"math"
"strconv"
"github.com/jordanorelli/hyperstone/bit"
"github.com/jordanorelli/hyperstone/dota"
)
const (
@ -14,80 +13,126 @@ const (
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)
}
func floatType(spec *typeSpec, env *Env) tÿpe {
switch spec.typeName {
case "CNetworkedQuantizedFloat":
return qFloatType(spec, env)
case "float32":
case "Vector":
default:
return nil
}
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 spec.encoder == "coord" {
Debug.Printf(" coord float type")
return coord_t
}
if flat.LowValue != nil || flat.HighValue != nil {
return nil, fmt.Errorf("float32 with a low or high value isn't supported")
if spec.serializer == "simulationtime" {
return nil
}
switch spec.bits {
case 0, 32:
Debug.Printf(" std float type")
return float_t
default:
return qFloatType(spec, env)
}
}
type_name := n.Symbol(int(flat.GetVarTypeSym())).String()
return &Primitive{name: type_name, read: readFloat32}, nil
var coord_t = &typeLiteral{
name: "coord",
newFn: func() value {
return new(coord_v)
},
}
func quantizedFloat(n *Namespace, flat *dota.ProtoFlattenedSerializerFieldT) (Type, error) {
if flat.LowValue == nil && flat.HighValue == nil {
return nil, fmt.Errorf("quantizedFloat has no boundaries")
}
type coord_v float32
bits := uint(flat.GetBitCount())
low, high := flat.GetLowValue(), flat.GetHighValue()
flags := int(flat.GetEncodeFlags())
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()
}
flags = flags & 7 // dunno how to handle -8 lol
steps := uint(1<<bits - 1) // total number of intervals
span := high - low // total range of values
step_width := span / float32(steps) // output width of each step
if span < 0 {
return nil, fmt.Errorf("invalid quantization span")
}
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
var special *float32
switch {
case flags&f_min > 0:
special = &low
case flags&f_max > 0:
special = &high
case flags&f_center > 0:
middle := (high + low) * 0.5
special = &middle
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")
}
if spec.high-spec.low < 0 {
return typeError("quantized float has invalid negative range")
}
t := &qfloat_t{typeSpec: *spec}
t.span = t.high - t.low
t.intervals = uint(1<<t.bits - 1)
t.interval = t.span / float32(t.intervals)
read := func(br bit.Reader, d *Dict) (interface{}, error) {
if special != nil && bit.ReadBool(br) {
return *special, nil
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)
}
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
Debug.Printf(" qfloat type")
return t
}
type qfloat_t struct {
typeSpec
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) nü() value { return &qfloat_v{t: t} }
func (t qfloat_t) typeName() string { return "qfloat" }
type qfloat_v struct {
t *qfloat_t
v float32
}
func (v qfloat_v) tÿpe() tÿpe { return v.t }
func (v *qfloat_v) read(r bit.Reader) error {
if v.t.special != nil && bit.ReadBool(r) {
v.v = *v.t.special
} else {
v.v = v.t.low + float32(r.ReadBits(v.t.bits))*v.t.interval
}
return r.Err()
}
// 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
func (v qfloat_v) String() string {
return strconv.FormatFloat(float64(v.v), 'f', 3, 32)
}

@ -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<<f.bits - 1)
// keep the inverse to mult instead of divide later
inv_steps := 1.0 / float32(steps)
// total range of values
span := f.high - f.low
if span < 0 {
panic("quantization span is backwards")
}
// output width of each step
step_width := span * inv_steps
var special *float32
switch {
case flags&f_min > 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)))
}

@ -0,0 +1,117 @@
package ent
import (
"fmt"
"strconv"
"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]
elemSpec := *spec
elemSpec.typeName = elemName
elem := parseTypeSpec(&elemSpec, env)
switch genericName {
case "CHandle", "CStrongHandle":
t := handle_t(fmt.Sprintf("%s<%s>", genericName, elem.typeName()))
return &t
case "CUtlVector":
return &cutl_vector_t{elem}
default:
return typeError("unknown generic name: %v", parts[0])
}
}
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 {
b_start = i
out[0] = strings.TrimSpace(string(runes[0:i]))
}
depth++
}
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")
}
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) nü() value {
return &cutl_vector{t: t}
}
func (t cutl_vector_t) typeName() string {
return fmt.Sprintf("CUtlVector<%s>", t.elem.typeName())
}
type cutl_vector struct {
t *cutl_vector_t
slots []value
}
func (v *cutl_vector) tÿpe() tÿpe { return v.t }
func (v *cutl_vector) read(r bit.Reader) error {
bit.ReadVarInt32(r) // ??
return r.Err()
}
func (v cutl_vector) String() string {
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)
}
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 {
if slot >= len(v.slots) {
return nil
}
return v.slots[slot]
}

@ -2,23 +2,38 @@ package ent
import (
"fmt"
"github.com/jordanorelli/hyperstone/bit"
)
type Handle struct{ name string }
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 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) Name() string { return h.name }
func (h *Handle) New(...interface{}) interface{} { return nil }
func (h *Handle) Slotted() bool { return false }
func (h handle) String() string {
return fmt.Sprintf("%s:%d", h.t.typeName(), h.id)
}
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)
func handleType(spec *typeSpec, env *Env) tÿpe {
if spec.typeName != "CGameSceneNodeHandle" {
return nil
}
return e, br.Err()
Debug.Printf(" handle type")
t := handle_t(spec.typeName)
return &t
}

@ -0,0 +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 hseq_t
}

@ -134,7 +134,9 @@ var hlist = nodeList{
r.add(int(bit.ReadUBitVarFP(br)) + 5)
}},
lNode{"PushOneLeftDeltaOneRightNonZero", 8, 2942, func(r *selectionReader, br bit.Reader) {
panic("not implemented: PushOneLeftDeltaOneRightNonZero")
Debug.Printf("PushOneLeftDeltaOneRightNonZero")
r.add(1)
r.push(int(bit.ReadUBitVarFP(br)))
}},
lNode{"PopAllButOnePlusOne", 29, 1837, func(r *selectionReader, br bit.Reader) {
r.pop(-1)
@ -266,4 +268,4 @@ var hlist = nodeList{
}},
}
var htree = makeTree(hlist)
var huffRoot = makeTree(hlist)

@ -66,5 +66,5 @@ func TestTree(t *testing.T) {
}
}
testWalk(htree, "")
testWalk(huffRoot, "")
}

@ -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)...)
}

@ -0,0 +1,64 @@
package ent
import (
"bytes"
"fmt"
"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
}
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()
}

@ -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
}

@ -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()
}

@ -5,70 +5,101 @@ import (
"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)
}
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)
}
if flat.BitCount == nil {
return nil, fmt.Errorf("dunno what to do when qangle type has no bitcount")
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
}
if flat.GetBitCount() < 0 {
return nil, fmt.Errorf("negative bit count wtf")
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 pitchYaw_t
default:
t := pitchYawAngles_t(spec.bits)
return &t
}
}
bits := uint(flat.GetBitCount())
switch bits {
switch spec.bits {
case 0:
return &Primitive{name: type_name, read: readQAngleCoords}, nil
Debug.Printf(" qangle type")
return qangle_t
case 32:
return &Primitive{name: type_name, read: readQAngleFloats}, nil
return nil
default:
return &Primitive{name: type_name, read: qangleReader(bits)}, nil
return 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()
}
var pitchYaw_t = &typeLiteral{
name: "qangle:pitchYaw",
newFn: func() value {
return new(pitchYaw_v)
},
}
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()
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 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()
func (v pitchYawAngles_v) String() string {
return fmt.Sprintf("qangle:pitchYawAngles{%f %f}", v.pitch, v.yaw)
}

@ -19,36 +19,37 @@ 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 {
func (s selection) fillSlots(v slotted, r bit.Reader) error {
return s.fillSlotsIter(0, v, v.tÿpe().typeName(), r)
}
func (s selection) fillSlotsIter(offset int, dest slotted, path string, r bit.Reader) error {
slot := s.vals[offset]
if s.count-offset <= 0 {
panic("selection makes no sense")
return fmt.Errorf("unable to fill selection %v having count %d at offset %d", s, s.count, offset)
}
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.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.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)
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.slotValue(slot)
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("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 fmt.Errorf("dest %s (%s) isn't slotted", dest.slotName(slot), dest.slotType(slot).typeName())
}
return s.fill(offset+1, fmt.Sprintf("%s.%s", displayPath, dest.slotName(slot)), vs, br)
return s.fillSlotsIter(offset+1, vs, fmt.Sprintf("%s.%s", path, dest.slotName(slot)), r)
}
}
@ -67,11 +68,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)
}

@ -1,28 +1,9 @@
package ent
import (
"fmt"
"github.com/jordanorelli/hyperstone/bit"
)
type slotted interface {
value
slotType(int) tÿpe
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
setSlotValue(int, value)
getSlotValue(int) value
}

@ -1,17 +1,10 @@
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 {
type symbol struct {
id int
table *SymbolTable
table *symbolTable
}
func (s Symbol) String() string { return (*s.table)[s.id] }
func (s symbol) String() string { return (*s.table)[s.id] }
type symbolTable []string

@ -1,99 +1,121 @@
package ent
import (
"bytes"
"fmt"
"github.com/jordanorelli/hyperstone/bit"
"github.com/jordanorelli/hyperstone/dota"
)
type decodeFn func(bit.Reader, *Dict) (interface{}, error)
type tÿpe interface {
nü() value
typeName() string
}
// 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{}
type typeLiteral struct {
name string
newFn func() value
}
// name is primarily of interest for debugging
Name() string
func (t typeLiteral) nü() value { return t.newFn() }
func (t typeLiteral) typeName() string { return t.name }
// whether or not the produced values are expected to be slotted.
Slotted() bool
type typeParseFn func(*typeSpec, *Env) tÿpe
// reads a value of this type off of the bit reader
Read(bit.Reader, *Dict) (interface{}, error)
func parseFieldType(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) tÿpe {
spec := new(typeSpec)
spec.fromProto(flat, env)
return parseTypeSpec(spec, env)
}
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
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 {
return t
}
}
return nil
}
return coalesce(arrayType, atomType, floatType, handleType, qAngleType,
hSeqType, genericType, vectorType, classType, unknownType)
}
if n.HasClass(type_name) {
Debug.Printf(" parseType: found class with name %s", type_name)
return nil, nil
}
type unknown_t string
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)
}
func (t unknown_t) typeName() string { return string(t) }
func (t *unknown_t) nü() value {
return &unknown_v{t: t}
}
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
type unknown_v struct {
t tÿpe
v uint64
}
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())))
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)
t := unknown_t(spec.typeName)
return &t
}
// 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.
func typeError(t string, args ...interface{}) tÿpe {
Debug.Printf(" type error: %s", fmt.Sprintf(t, args...))
return error_t(fmt.Sprintf(t, args...))
}
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) }
type typeSpec struct {
name string
typeName string
bits uint
low float32
high float32
flags int
serializer string
serializerV int
send string
encoder string
}
func (s *typeSpec) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, env *Env) {
s.name = env.symbol(int(flat.GetVarNameSym()))
s.typeName = env.symbol(int(flat.GetVarTypeSym()))
if flat.GetBitCount() < 0 {
// this would cause ridiculously long reads later if we let it overflow
panic("negative bit count: data is likely corrupt")
}
if ff.FieldSerializerVersion != nil {
fmt.Fprintf(&buf, " version: %d", ff.GetFieldSerializerVersion())
s.bits = uint(flat.GetBitCount())
s.low = flat.GetLowValue()
s.high = flat.GetHighValue()
s.flags = int(flat.GetEncodeFlags())
if flat.FieldSerializerNameSym != nil {
s.serializer = env.symbol(int(*flat.FieldSerializerNameSym))
}
if ff.SendNodeSym != nil {
fmt.Fprintf(&buf, " send: %s", n.Symbol(int(ff.GetSendNodeSym())))
s.serializerV = int(flat.GetFieldSerializerVersion())
if flat.SendNodeSym != nil {
s.send = env.symbol(int(*flat.SendNodeSym))
}
if ff.VarEncoderSym != nil {
fmt.Fprintf(&buf, " encoder: %s", n.Symbol(int(ff.GetVarEncoderSym())))
if flat.VarEncoderSym != nil {
s.encoder = env.symbol(int(*flat.VarEncoderSym))
}
fmt.Fprintf(&buf, "}")
return buf.String()
}

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

@ -0,0 +1,11 @@
package ent
import (
"github.com/jordanorelli/hyperstone/bit"
)
type value interface {
String() string
tÿpe() tÿpe
read(bit.Reader) error
}

@ -1,7 +1,67 @@
package ent
import (
"fmt"
"github.com/jordanorelli/hyperstone/bit"
)
func vectorType(spec *typeSpec, env *Env) tÿpe {
if spec.encoder != "" {
return nil
}
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 {
elem tÿpe
}
func (t vector_t) typeName() string {
return fmt.Sprintf("vector<%s>", t.elem.typeName())
}
func (t *vector_t) nü() value {
return &vector{t: t}
}
type vector struct {
x float32
y float32
z float32
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
}
}
return nil
}
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)
}

@ -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])
}

@ -142,11 +142,11 @@ func main() {
handle = dumpClasses
case "entities":
ent.Debug = log.New(os.Stdout, "", 0)
sd := stbl.NewDict()
ed := ent.NewDict(sd)
env := ent.NewEnv()
handle = func(m proto.Message) {
sd.Handle(m)
ed.Handle(m)
if err := env.Handle(m); err != nil {
bail(1, "%v", err)
}
}
default:
bail(1, "no such action: %s", flag.Arg(0))

@ -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 {

Loading…
Cancel
Save