decompress right pl0x

master
Jordan Orelli 8 years ago
parent ac944ad6be
commit 7e088dfe61

@ -2,9 +2,9 @@ package main
import ( import (
"fmt" "fmt"
"io"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/golang/snappy"
"github.com/jordanorelli/hyperstone/bit" "github.com/jordanorelli/hyperstone/bit"
"github.com/jordanorelli/hyperstone/dota" "github.com/jordanorelli/hyperstone/dota"
) )
@ -12,17 +12,16 @@ import (
// datagram represents the top-level envelope in the dota replay format. All // datagram represents the top-level envelope in the dota replay format. All
// data in the replay file is packed into datagram frames of at most 65kb. // data in the replay file is packed into datagram frames of at most 65kb.
type dataGram struct { type dataGram struct {
cmd dota.EDemoCommands cmd dota.EDemoCommands
tick int64 tick int64
compressed bool body []byte
body []byte
} }
func (g dataGram) String() string { func (g dataGram) String() string {
if len(g.body) > 30 { if len(g.body) > 30 {
return fmt.Sprintf("{dataGram cmd: %v tick: %v compressed: %t size: %d body: %q...}", g.cmd, g.tick, g.compressed, len(g.body), g.body[:27]) return fmt.Sprintf("{dataGram cmd: %v tick: %v size: %d body: %q...}", g.cmd, g.tick, len(g.body), g.body[:27])
} }
return fmt.Sprintf("{dataGram cmd: %v tick: %v compressed: %t size: %d body: %q}", g.cmd, g.tick, g.compressed, len(g.body), g.body) return fmt.Sprintf("{dataGram cmd: %v tick: %v size: %d body: %q}", g.cmd, g.tick, len(g.body), g.body)
} }
func (g *dataGram) check(dump bool) error { func (g *dataGram) check(dump bool) error {
@ -30,42 +29,36 @@ func (g *dataGram) check(dump bool) error {
return fmt.Errorf("wrong command type in openPacket: %v", g.cmd) return fmt.Errorf("wrong command type in openPacket: %v", g.cmd)
} }
if g.compressed {
buf, err := snappy.Decode(nil, g.body)
if err != nil {
return wrap(err, "open packet error: could not decode body")
}
g.body = buf
g.compressed = false
}
packet := new(dota.CDemoPacket) packet := new(dota.CDemoPacket)
if err := proto.Unmarshal(g.body, packet); err != nil { if err := proto.Unmarshal(g.body, packet); err != nil {
return wrap(err, "onPacket unable to unmarshal message body") return wrap(err, "onPacket unable to unmarshal message body")
} }
if dump { br := bit.NewBytesReader(packet.GetData())
br := bit.NewBytesReader(packet.GetData()) for {
for { t := br.ReadUBitVar()
t := br.ReadUBitVar() s := br.ReadVarInt()
s := br.ReadVarInt() b := make([]byte, s)
b := make([]byte, s) br.Read(b)
br.Read(b) switch err := br.Err(); err {
if br.Err() != nil { case nil:
break break
} case io.EOF:
return nil
default:
return err
}
if dump {
fmt.Printf("\t%v\n", entity{t: uint32(t), size: uint32(s), body: b}) fmt.Printf("\t%v\n", entity{t: uint32(t), size: uint32(s), body: b})
e := entFactory.BuildMessage(int(t)) }
if e == nil { e := entFactory.BuildMessage(int(t))
fmt.Printf("\tno known entity for type id %d\n", int(t)) if e == nil {
continue fmt.Printf("\tno known entity for type id %d\n", int(t))
} continue
err := proto.Unmarshal(b, e) }
if err != nil { err := proto.Unmarshal(b, e)
fmt.Printf("entity unmarshal error: %v\n", err) if err != nil {
} else { fmt.Printf("entity unmarshal error: %v\n", err)
fmt.Printf("\t\t%v\n", e)
}
} }
} }
return nil return nil

@ -40,11 +40,11 @@ var (
"EDotaUserMessages_DOTA_UM_": "CDOTAUserMsg_", "EDotaUserMessages_DOTA_UM_": "CDOTAUserMsg_",
} }
specials = map[string]string{ specials = map[string]string{
"EDemoCommands_DEM_SignonPacket": "CDemoPacket",
"EDotaUserMessages_DOTA_UM_StatsHeroDetails": "CDOTAUserMsg_StatsHeroMinuteDetails", "EDotaUserMessages_DOTA_UM_StatsHeroDetails": "CDOTAUserMsg_StatsHeroMinuteDetails",
"EDotaUserMessages_DOTA_UM_CombatLogDataHLTV": "CMsgDOTACombatLogEntry", "EDotaUserMessages_DOTA_UM_CombatLogDataHLTV": "CMsgDOTACombatLogEntry",
"EDotaUserMessages_DOTA_UM_TournamentDrop": "CMsgGCToClientTournamentItemDrop", "EDotaUserMessages_DOTA_UM_TournamentDrop": "CMsgGCToClientTournamentItemDrop",
"EDotaUserMessages_DOTA_UM_MatchMetadata": "CDOTAClientMsg_MatchMetadata", "EDotaUserMessages_DOTA_UM_MatchMetadata": "CDOTAClientMsg_MatchMetadata",
"ETEProtobufIds_TE_EffectDispatchId": "CMsgTEEffectDispatch",
} }
skipped = map[string]bool{ skipped = map[string]bool{
"EDemoCommands_DEM_IsCompressed": true, "EDemoCommands_DEM_IsCompressed": true,
@ -59,31 +59,30 @@ var (
} }
// EBaseUserMessages_UM_HandHapticPulse // EBaseUserMessages_UM_HandHapticPulse
tpl = `package main tpl = `package main
/*------------------------------------------------------------------------------
.aMMMb .aMMMb dMMMMb dMMMMMP ////////////////////////////////////////////////////////////////////////////////
dMP"VMP dMP"dMP dMP VMP dMP //
dMP dMP dMP dMP dMP dMMMP // .aMMMb .aMMMb dMMMMb dMMMMMP
dMP.aMP dMP.aMP dMP.aMP dMP // dMP"VMP dMP"dMP dMP VMP dMP
VMMMP" VMMMP" dMMMMP" dMMMMMP // dMP dMP dMP dMP dMP dMMMP
// dMP.aMP dMP.aMP dMP.aMP dMP
.aMMMMP dMMMMMP dMMMMb dMMMMMP dMMMMb .aMMMb dMMMMMMP dMMMMMP dMMMMb // VMMMP" VMMMP" dMMMMP" dMMMMMP
dMP" dMP dMP dMP dMP dMP.dMP dMP"dMP dMP dMP dMP VMP //
dMP MMP"dMMMP dMP dMP dMMMP dMMMMK" dMMMMMP dMP dMMMP dMP dMP // .aMMMMP dMMMMMP dMMMMb dMMMMMP dMMMMb .aMMMb dMMMMMMP dMMMMMP dMMMMb
dMP.dMP dMP dMP dMP dMP dMP"AMF dMP dMP dMP dMP dMP.aMP // dMP" dMP dMP dMP dMP dMP.dMP dMP"dMP dMP dMP dMP VMP
VMMMP" dMMMMMP dMP dMP dMMMMMP dMP dMP dMP dMP dMP dMMMMMP dMMMMP" // dMP MMP"dMMMP dMP dMP dMMMP dMMMMK" dMMMMMP dMP dMMMP dMP dMP
// dMP.dMP dMP dMP dMP dMP dMP"AMF dMP dMP dMP dMP dMP.aMP
// VMMMP" dMMMMMP dMP dMP dMMMMMP dMP dMP dMP dMP dMP dMMMMMP dMMMMP"
This code was generated by a code-generation program. It was NOT written by //
hand. Do not edit this file by hand! Your edits will be destroyed! //
// This code was generated by a code-generation program. It was NOT written by
This file can be regenerated by running "go generate" // hand. Do not edit this file by hand! Your edits will be destroyed!
//
The generator program is defined in "gen/main.go" // This file can be regenerated by running "go generate"
//
------------------------------------------------------------------------------*/ // The generator program is defined in "gen/main.go"
//
////////////////////////////////////////////////////////////////////////////////
import ( import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"

@ -1,28 +1,28 @@
package main package main
/*------------------------------------------------------------------------------ ////////////////////////////////////////////////////////////////////////////////
//
.aMMMb .aMMMb dMMMMb dMMMMMP // .aMMMb .aMMMb dMMMMb dMMMMMP
dMP"VMP dMP"dMP dMP VMP dMP // dMP"VMP dMP"dMP dMP VMP dMP
dMP dMP dMP dMP dMP dMMMP // dMP dMP dMP dMP dMP dMMMP
dMP.aMP dMP.aMP dMP.aMP dMP // dMP.aMP dMP.aMP dMP.aMP dMP
VMMMP" VMMMP" dMMMMP" dMMMMMP // VMMMP" VMMMP" dMMMMP" dMMMMMP
//
.aMMMMP dMMMMMP dMMMMb dMMMMMP dMMMMb .aMMMb dMMMMMMP dMMMMMP dMMMMb // .aMMMMP dMMMMMP dMMMMb dMMMMMP dMMMMb .aMMMb dMMMMMMP dMMMMMP dMMMMb
dMP" dMP dMP dMP dMP dMP.dMP dMP"dMP dMP dMP dMP VMP // dMP" dMP dMP dMP dMP dMP.dMP dMP"dMP dMP dMP dMP VMP
dMP MMP"dMMMP dMP dMP dMMMP dMMMMK" dMMMMMP dMP dMMMP dMP dMP // dMP MMP"dMMMP dMP dMP dMMMP dMMMMK" dMMMMMP dMP dMMMP dMP dMP
dMP.dMP dMP dMP dMP dMP dMP"AMF dMP dMP dMP dMP dMP.aMP // dMP.dMP dMP dMP dMP dMP dMP"AMF dMP dMP dMP dMP dMP.aMP
VMMMP" dMMMMMP dMP dMP dMMMMMP dMP dMP dMP dMP dMP dMMMMMP dMMMMP" // VMMMP" dMMMMMP dMP dMP dMMMMMP dMP dMP dMP dMP dMP dMMMMMP dMMMMP"
//
//
This code was generated by a code-generation program. It was NOT written by // This code was generated by a code-generation program. It was NOT written by
hand. Do not edit this file by hand! Your edits will be destroyed! // hand. Do not edit this file by hand! Your edits will be destroyed!
//
This file can be regenerated by running "go generate" // This file can be regenerated by running "go generate"
//
The generator program is defined in "gen/main.go" // The generator program is defined in "gen/main.go"
//
------------------------------------------------------------------------------*/ ////////////////////////////////////////////////////////////////////////////////
import ( import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
@ -48,7 +48,6 @@ var cmdFactory = protoFactory{
5: func() proto.Message { return new(dota.CDemoClassInfo) }, 5: func() proto.Message { return new(dota.CDemoClassInfo) },
6: func() proto.Message { return new(dota.CDemoStringTables) }, 6: func() proto.Message { return new(dota.CDemoStringTables) },
7: func() proto.Message { return new(dota.CDemoPacket) }, 7: func() proto.Message { return new(dota.CDemoPacket) },
8: func() proto.Message { return new(dota.CDemoPacket) },
9: func() proto.Message { return new(dota.CDemoConsoleCmd) }, 9: func() proto.Message { return new(dota.CDemoConsoleCmd) },
10: func() proto.Message { return new(dota.CDemoCustomData) }, 10: func() proto.Message { return new(dota.CDemoCustomData) },
11: func() proto.Message { return new(dota.CDemoCustomDataCallbacks) }, 11: func() proto.Message { return new(dota.CDemoCustomDataCallbacks) },

@ -49,7 +49,7 @@ type options struct {
f string // input file f string // input file
memprofile string memprofile string
cpuprofile string cpuprofile string
messages bool // dump messages or no datagrams bool // dump datagrams or no
packets bool // dump packets or no packets bool // dump packets or no
} }
@ -105,7 +105,7 @@ func main() {
flag.BoolVar(&opts.b, "b", false, "input is expected to be bzip-compressed") flag.BoolVar(&opts.b, "b", false, "input is expected to be bzip-compressed")
flag.BoolVar(&opts.v, "v", false, "verbose mode") flag.BoolVar(&opts.v, "v", false, "verbose mode")
flag.StringVar(&opts.f, "f", "--", "input file to be used. -- means stdin") flag.StringVar(&opts.f, "f", "--", "input file to be used. -- means stdin")
flag.BoolVar(&opts.messages, "messages", false, "dump top-level messages to stdout") flag.BoolVar(&opts.datagrams, "datagrams", false, "dump top-level datagram info to stdout")
flag.BoolVar(&opts.packets, "packets", false, "dump packets to stdout") flag.BoolVar(&opts.packets, "packets", false, "dump packets to stdout")
flag.StringVar(&opts.memprofile, "memprofile", "", "memory profile destination") flag.StringVar(&opts.memprofile, "memprofile", "", "memory profile destination")
flag.StringVar(&opts.cpuprofile, "cpuprofile", "", "cpu profile destination") flag.StringVar(&opts.cpuprofile, "cpuprofile", "", "cpu profile destination")
@ -125,7 +125,7 @@ func main() {
} }
p := newParser(r) p := newParser(r)
p.dumpMessages = opts.messages p.dumpDatagrams = opts.datagrams
p.dumpPackets = opts.packets p.dumpPackets = opts.packets
if err := p.start(); err != nil { if err := p.start(); err != nil {
bail(1, "parse error: %v", err) bail(1, "parse error: %v", err)

@ -4,8 +4,10 @@ import (
"bufio" "bufio"
"fmt" "fmt"
"io" "io"
"reflect"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/golang/snappy"
"github.com/jordanorelli/hyperstone/dota" "github.com/jordanorelli/hyperstone/dota"
) )
@ -13,8 +15,8 @@ type parser struct {
// the source of replay bytes. Must NOT be compressed. // the source of replay bytes. Must NOT be compressed.
source *bufio.Reader source *bufio.Reader
dumpMessages bool dumpDatagrams bool
dumpPackets bool dumpPackets bool
} }
func newParser(r io.Reader) *parser { func newParser(r io.Reader) *parser {
@ -38,26 +40,29 @@ func (p *parser) start() error {
func (p *parser) run() error { func (p *parser) run() error {
for { for {
msg, err := p.readMessage() gram, err := p.readDatagram()
if err != nil { if err != nil {
return wrap(err, "read message error in run loop") return wrap(err, "read datagram error in run loop")
} }
if p.dumpMessages { if p.dumpDatagrams {
fmt.Println(msg) fmt.Println(gram)
} }
switch msg.cmd {
if len(gram.body) == 0 {
continue
}
switch gram.cmd {
case dota.EDemoCommands_DEM_Packet: case dota.EDemoCommands_DEM_Packet:
if err := msg.check(p.dumpPackets); err != nil { if err := gram.check(p.dumpPackets); err != nil {
fmt.Printf("error: %v\n", err) fmt.Printf("error: %v\n", err)
} }
default: default:
m := cmdFactory.BuildMessage(int(msg.cmd)) m := cmdFactory.BuildMessage(int(gram.cmd))
if m != nil { if m != nil {
err := proto.Unmarshal(msg.body, m) err := proto.Unmarshal(gram.body, m)
if err != nil { if err != nil {
fmt.Printf("cmd unmarshal error: %v\n", err) fmt.Printf("cmd unmarshal error unpacking data of length %d with cmd type %s into message type %v: %v\n", len(gram.body), gram.cmd, reflect.TypeOf(m), err)
} else {
fmt.Println(m)
} }
} }
} }
@ -133,31 +138,39 @@ func (p *parser) readCommand() (dota.EDemoCommands, bool, error) {
return dota.EDemoCommands(n), compressed, nil return dota.EDemoCommands(n), compressed, nil
} }
func (p *parser) readMessage() (*dataGram, error) { func (p *parser) readDatagram() (*dataGram, error) {
cmd, compressed, err := p.readCommand() cmd, compressed, err := p.readCommand()
if err != nil { if err != nil {
return nil, wrap(err, "readMessage couldn't get a command") return nil, wrap(err, "readDatagram couldn't get a command")
} }
tick, err := p.decodeVarint() tick, err := p.decodeVarint()
if err != nil { if err != nil {
return nil, wrap(err, "readMessage couldn't read the tick value") return nil, wrap(err, "readDatagram couldn't read the tick value")
} }
size, err := p.decodeVarint() size, err := p.decodeVarint()
if err != nil { if err != nil {
return nil, wrap(err, "readMessage couldn't read the size value") return nil, wrap(err, "readDatagram couldn't read the size value")
} }
if size > 0 { if size > 0 {
buf := make([]byte, int(size)) buf := make([]byte, int(size))
if _, err := io.ReadFull(p.source, buf); err != nil { if _, err := io.ReadFull(p.source, buf); err != nil {
return nil, wrap(err, "readMessage couldn't read message body") return nil, wrap(err, "readDatagram couldn't read datagram body")
}
if compressed {
var err error
buf, err = snappy.Decode(nil, buf)
if err != nil {
return nil, wrap(err, "readDatagram couldn't snappy decode body")
}
} }
// TODO: pool these! // TODO: pool these!
return &dataGram{cmd, int64(tick), compressed, buf}, nil return &dataGram{cmd, int64(tick), buf}, nil
} }
// TODO: pool these! // TODO: pool these!
return &dataGram{cmd, int64(tick), compressed, nil}, nil return &dataGram{cmd, int64(tick), nil}, nil
} }

Loading…
Cancel
Save