parent
16aa368ca1
commit
f150e8aff1
@ -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 }
|
|
@ -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]
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 +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 }
|
|
@ -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)
|
||||||
|
}
|
@ -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 }
|
|
@ -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<<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")
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
@ -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)))
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -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] }
|
|
@ -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()
|
|
||||||
}
|
|
@ -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)
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package ent
|
|
||||||
|
|
||||||
type vector struct {
|
|
||||||
x float32
|
|
||||||
y float32
|
|
||||||
z float32
|
|
||||||
}
|
|
@ -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])
|
|
||||||
}
|
|
Loading…
Reference in New Issue