looking at send tables

master
Jordan Orelli 8 years ago
parent 82dbc58f58
commit b73b917084

@ -6,7 +6,10 @@ import (
"github.com/jordanorelli/hyperstone/dota" "github.com/jordanorelli/hyperstone/dota"
) )
// classInfo container contains info about entity classes found in a given
// replay
type classInfo struct { type classInfo struct {
names map[int]string
} }
func (c *classInfo) handle(m proto.Message) { func (c *classInfo) handle(m proto.Message) {
@ -17,5 +20,6 @@ func (c *classInfo) handle(m proto.Message) {
for _, class := range v.GetClasses() { for _, class := range v.GetClasses() {
fmt.Printf("class-id: %d network-name: %s table-name: %s\n", class.GetClassId(), class.GetNetworkName(), class.GetTableName()) fmt.Printf("class-id: %d network-name: %s table-name: %s\n", class.GetClassId(), class.GetNetworkName(), class.GetTableName())
c.names[int(class.GetClassId())] = class.GetNetworkName()
} }
} }

@ -0,0 +1,74 @@
package dt
import (
"bytes"
"fmt"
"github.com/jordanorelli/hyperstone/dota"
)
type Field struct {
_type Symbol
name Symbol
sendNode Symbol
bits *int
low *float32
high *float32
flags *int32
serializer *Symbol
serializerVersion *int32
encoder *Symbol
}
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 != nil {
fmt.Fprintf(&buf, " bits: %d", *f.bits)
}
if f.low != nil {
fmt.Fprintf(&buf, " low: %f", *f.low)
}
if f.high != nil {
fmt.Fprintf(&buf, " high: %f", *f.high)
}
if f.flags != nil {
fmt.Fprintf(&buf, " flags: %d", *f.flags)
}
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()))
if flat.BitCount == nil {
f.bits = nil
} else {
f.bits = new(int)
*f.bits = int(flat.GetBitCount())
}
f.low = flat.LowValue
f.high = flat.HighValue
f.flags = flat.EncodeFlags
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))
}

@ -0,0 +1,20 @@
package dt
import (
"github.com/jordanorelli/hyperstone/dota"
)
type Serializer struct {
Name Symbol
Version int
Fields []*Field
}
func (s *Serializer) fromProto(v *dota.ProtoFlattenedSerializerT, st *SymbolTable, fields []Field) {
s.Name = st.Symbol(int(v.GetSerializerNameSym()))
s.Version = int(v.GetSerializerVersion())
s.Fields = make([]*Field, len(v.GetFieldsIndex()))
for i, fi := range v.GetFieldsIndex() {
s.Fields[i] = &fields[fi]
}
}

@ -0,0 +1,17 @@
package dt
// 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] }

@ -0,0 +1,56 @@
package dt
import (
"fmt"
"io"
"github.com/jordanorelli/hyperstone/dota"
)
// TableSet represents a collection of tables.
type TableSet struct {
SymbolTable
Fields []Field
Serializers []Serializer
}
func (t *TableSet) DebugPrint(w io.Writer) {
fmt.Fprintln(w, "Symbols:")
for _, sym := range t.SymbolTable {
fmt.Fprintf(w, "\t%s\n", sym)
}
fmt.Fprintln(w, "Fields:")
for _, f := range t.Fields {
fmt.Fprintf(w, "\t%s\n", f)
}
fmt.Fprintln(w, "Serializers:")
for _, s := range t.Serializers {
fmt.Fprintf(w, "\t%s (%d):\n", s.Name, s.Version)
for _, f := range s.Fields {
fmt.Fprintf(w, "\t\t%s\n", f)
}
}
}
// ParseFlattened parses a flattened TableSet definition, as defined by the
// Dota replay protobufs.
func ParseFlattened(m *dota.CSVCMsg_FlattenedSerializer) *TableSet {
ts := &TableSet{SymbolTable: SymbolTable(m.GetSymbols())}
ts.parseFields(m.GetFields())
ts.parseSerializers(m.GetSerializers())
return ts
}
func (ts *TableSet) parseFields(flat []*dota.ProtoFlattenedSerializerFieldT) {
ts.Fields = make([]Field, len(flat))
for i, f := range flat {
ts.Fields[i].fromProto(f, &ts.SymbolTable)
}
}
func (ts *TableSet) parseSerializers(flat []*dota.ProtoFlattenedSerializerT) {
ts.Serializers = make([]Serializer, len(flat))
for i, s := range flat {
ts.Serializers[i].fromProto(s, &ts.SymbolTable, ts.Fields)
}
}

@ -0,0 +1,31 @@
package main
import (
"fmt"
"github.com/golang/protobuf/proto"
"github.com/jordanorelli/hyperstone/dota"
)
// type CSVCMsg_PacketEntities struct {
// MaxEntries *int32
// UpdatedEntries *int32
// IsDelta *bool
// UpdateBaseline *bool
// Baseline *int32
// DeltaFrom *int32
// EntityData []byte
// PendingFullFrame *bool
// ActiveSpawngroupHandle *uint32
// MaxSpawngroupCreationsequence *uint32
// }
func dumpEntities(m proto.Message) {
switch v := m.(type) {
case *dota.CSVCMsg_PacketEntities:
data := v.GetEntityData()
if len(data) > 32 {
data = data[:32]
}
fmt.Printf("{MaxEntries: %d UpdatedEntries: %v IsDelta: %t UpdateBaseline: %t Baseline: %d DeltaFrom: %d EntityData: %x PendingFullFrame: %t ActiveSpawngroupHandle: %d}\n", v.GetMaxEntries(), v.GetUpdatedEntries(), v.GetIsDelta(), v.GetUpdateBaseline(), v.GetBaseline(), v.GetDeltaFrom(), data, v.GetPendingFullFrame(), v.GetActiveSpawngroupHandle())
}
}

@ -124,12 +124,16 @@ func main() {
handle = printTypes handle = printTypes
case "pretty": case "pretty":
handle = prettyPrint handle = prettyPrint
case "send-tables":
handle = sendTables
case "string-tables": case "string-tables":
st := newStringTables() st := newStringTables()
handle = st.handle handle = st.handle
case "class-info": case "class-info":
ci := new(classInfo) ci := new(classInfo)
handle = ci.handle handle = ci.handle
case "entities":
handle = dumpEntities
default: default:
bail(1, "no such action: %s", flag.Arg(0)) bail(1, "no such action: %s", flag.Arg(0))
} }

@ -0,0 +1,38 @@
package main
import (
"fmt"
"os"
"github.com/golang/protobuf/proto"
"github.com/jordanorelli/hyperstone/bit"
"github.com/jordanorelli/hyperstone/dota"
"github.com/jordanorelli/hyperstone/dt"
)
func sendTables(m proto.Message) {
v, ok := m.(*dota.CDemoSendTables)
if !ok {
return
}
// sendtables only has one field, a binary data field.
data := v.GetData()
br := bit.NewBytesReader(data)
// body is length-prefixed
size := int(bit.ReadVarInt(br))
buf := make([]byte, size)
br.Read(buf)
serializer := dota.CSVCMsg_FlattenedSerializer{}
if err := proto.Unmarshal(buf, &serializer); err != nil {
fmt.Printf("error: %v\n", err)
return
}
ts := dt.ParseFlattened(&serializer)
ts.DebugPrint(os.Stdout)
}

@ -2,11 +2,13 @@ package main
import ( import (
"fmt" "fmt"
"os"
"unicode/utf8"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/golang/snappy" "github.com/golang/snappy"
"github.com/jordanorelli/hyperstone/bit" "github.com/jordanorelli/hyperstone/bit"
"github.com/jordanorelli/hyperstone/dota" "github.com/jordanorelli/hyperstone/dota"
"os"
) )
const ( const (
@ -101,10 +103,15 @@ func (s stringTableEntry) String() string {
if s.value == nil { if s.value == nil {
return fmt.Sprintf("{%s nil}", s.key) return fmt.Sprintf("{%s nil}", s.key)
} }
if utf8.Valid(s.value) {
return fmt.Sprintf("{%s %s}", s.key, s.value)
}
if len(s.value) > 32 { if len(s.value) > 32 {
return fmt.Sprintf("{%s %x}", s.key, s.value[:32]) return fmt.Sprintf("{%s 0x%x}", s.key, s.value[:32])
} }
return fmt.Sprintf("{%s %x}", s.key, s.value) return fmt.Sprintf("{%s 0x%x}", s.key, s.value)
} }
func (s *stringTables) handle(m proto.Message) { func (s *stringTables) handle(m proto.Message) {

Loading…
Cancel
Save