protobuf reflection
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
*.dem
|
*.dem
|
||||||
*.un~
|
*.un~
|
||||||
|
.vscode/
|
||||||
|
|||||||
35
main.go
35
main.go
@@ -15,31 +15,28 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r := bufio.NewReader(f)
|
r := bufio.NewReader(f)
|
||||||
|
r.Discard(16)
|
||||||
|
|
||||||
size, err := parse.First(r)
|
for i, err := r.Peek(1); len(i) == 1 && err == nil; {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := r.Discard(int(size) - 12); err != nil {
|
|
||||||
fmt.Println(fmt.Errorf("error jumping to last frame: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
frame, err := parse.DecodeNextFrame(r)
|
frame, err := parse.DecodeNextFrame(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(fmt.Errorf("error parsing frame: %v", err))
|
fmt.Println(fmt.Errorf("error parsing frame: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if frame != nil {
|
||||||
|
fmt.Printf("message: %+v", frame.Message)
|
||||||
|
fmt.Printf("\n\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Printf("%+v", frame.Message)
|
// offset, err := parse.First(r)
|
||||||
|
// if err != nil {
|
||||||
for _, player := range frame.Message.GameInfo.Dota.PlayerInfo {
|
// fmt.Println(err)
|
||||||
fmt.Println(*player.HeroName)
|
// }
|
||||||
fmt.Println(*player.PlayerName)
|
// _ = offset // remove later
|
||||||
fmt.Println("Steam:", *player.Steamid)
|
|
||||||
fmt.Println("")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//fmt.Printf("%+v", )
|
|
||||||
|
|
||||||
|
// if _, err := r.Discard(int(offset) - 12); err != nil {
|
||||||
|
// fmt.Println(fmt.Errorf("error jumping to last frame: %v", err))
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package parse
|
package parse
|
||||||
|
|
||||||
const SIGNATURE = "PBDEMS2\x00"
|
const SOURCE2_SIGN = "PBDEMS2\x00"
|
||||||
|
|||||||
154
parse/parse.go
154
parse/parse.go
@@ -3,6 +3,7 @@ package parse
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
@@ -12,71 +13,95 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
Kind uint64 `json:"kind"` // see demo.proto
|
Kind demo.EDemoCommands `json:"kind"` // see demo.proto
|
||||||
Tick uint64 `json:"tick"` // time elapsed in replay time
|
Tick uint64 `json:"tick"` // time elapsed in replay time
|
||||||
Size uint64 `json:"size"` // size of message in bytes
|
Message proto.Message `json:"message"` // protobuf encoded message (may be compressed with snappy)
|
||||||
Message *demo.CDemoFileInfo `json:"message"` // protobuf encoded message (may be compressed with snappy)
|
HasEmbeddedData bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// If kind has a certain bit set, the frame message is compressed
|
// Return true if the frame message is snappy compressed
|
||||||
// returns true if frame is compressed
|
func (frame *Frame) isCompressed() bool {
|
||||||
func isCompressed(frame *Frame) bool {
|
return (frame.Kind&demo.EDemoCommands_DEM_IsCompressed != 0)
|
||||||
return (frame.Kind&uint64(demo.EDemoCommands_DEM_IsCompressed) != 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if file header is correct and returns number the address of the last frame
|
// Checks if file header is correct and returns number the address of the last frame
|
||||||
func First(r *bufio.Reader) (uint64, error) {
|
func First(r *bufio.Reader) (uint64, error) {
|
||||||
// Check if first 8 bytes of file matches the source 2 replay file header
|
// Check if first 8 bytes of file is PBDEMS2\0
|
||||||
header := make([]byte, 8)
|
header := make([]byte, 8)
|
||||||
if _, err := io.ReadFull(r, header); err != nil {
|
if _, err := io.ReadFull(r, header); err != nil {
|
||||||
return 0, fmt.Errorf("error when reading file: %v", err)
|
return 0, fmt.Errorf("error when reading file: %v", err)
|
||||||
}
|
}
|
||||||
if string(header) != SIGNATURE {
|
if string(header) != SOURCE2_SIGN {
|
||||||
return 0, fmt.Errorf("wrong file signature: %v, should be ", SIGNATURE)
|
return 0, fmt.Errorf("wrong file signature: %v, should be ", SOURCE2_SIGN)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read last frame
|
// Read offset to last frame
|
||||||
var offset uint32
|
var offset uint32
|
||||||
err := binary.Read(r, binary.LittleEndian, &offset)
|
err := binary.Read(r, binary.LittleEndian, &offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("error reading number of frames in replay: %v", err)
|
return 0, fmt.Errorf("error reading number of frames in replay: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// remove later
|
|
||||||
println("size of demo is", offset, "frames")
|
|
||||||
|
|
||||||
return uint64(offset), nil
|
return uint64(offset), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses the next frame on the reader
|
// Parses the next frame on the reader
|
||||||
func DecodeNextFrame(r *bufio.Reader) (*Frame, error) {
|
func DecodeNextFrame(r *bufio.Reader) (*Frame, error) {
|
||||||
// Read kind, tick, size and message
|
|
||||||
frame := new(Frame)
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if frame.Kind, err = binary.ReadUvarint(r); err != nil {
|
// Read command
|
||||||
return nil, fmt.Errorf("error reading frame kind for frame: %v", err)
|
c, err := binary.ReadUvarint(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading frame command: %v", err)
|
||||||
|
}
|
||||||
|
command := demo.EDemoCommands(c)
|
||||||
|
|
||||||
|
// Read tick
|
||||||
|
tick, err := binary.ReadUvarint(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading frame tick: %v", err)
|
||||||
|
}
|
||||||
|
if tick == 0xFFFFFFFF {
|
||||||
|
tick = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if frame.Tick, err = binary.ReadUvarint(r); err != nil {
|
// Read size
|
||||||
return nil, fmt.Errorf("error reading frame tick for frame: %v", err)
|
size, err := binary.ReadUvarint(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading frame size: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if frame.Size, err = binary.ReadUvarint(r); err != nil {
|
// Instanciate frame with data
|
||||||
return nil, fmt.Errorf("error reading frame size for frame: %v", err)
|
frame := &Frame{
|
||||||
|
Kind: command,
|
||||||
|
Tick: tick,
|
||||||
|
Message: nil,
|
||||||
|
HasEmbeddedData: false,
|
||||||
|
}
|
||||||
|
frame.setMessageType()
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
if !frame.HasEmbeddedData {
|
||||||
|
fmt.Printf(`
|
||||||
|
kind: %d
|
||||||
|
tick: %d
|
||||||
|
size: %d
|
||||||
|
`, command & ^demo.EDemoCommands_DEM_IsCompressed, tick, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
message := make([]byte, frame.Size)
|
if frame.HasEmbeddedData {
|
||||||
|
r.Discard(int(size))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read message
|
||||||
|
message := make([]byte, size)
|
||||||
io.ReadFull(r, message)
|
io.ReadFull(r, message)
|
||||||
|
|
||||||
frame.Message = new(demo.CDemoFileInfo)
|
if frame.isCompressed() {
|
||||||
|
|
||||||
if isCompressed(frame) {
|
|
||||||
decoded, err := snappy.Decode(nil, message)
|
decoded, err := snappy.Decode(nil, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error decoding message: %v", err)
|
return nil, fmt.Errorf("error decoding message: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println("I got decompressed :O")
|
|
||||||
proto.Unmarshal(decoded, frame.Message)
|
proto.Unmarshal(decoded, frame.Message)
|
||||||
} else {
|
} else {
|
||||||
proto.Unmarshal(message, frame.Message)
|
proto.Unmarshal(message, frame.Message)
|
||||||
@@ -85,14 +110,73 @@ func DecodeNextFrame(r *bufio.Reader) (*Frame, error) {
|
|||||||
return frame, nil
|
return frame, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads every single frame (probably not very efficient)
|
// I hate reflection
|
||||||
func DecodeAllFrames(r *bufio.Reader, frameCount uint64) ([]*Frame, error) {
|
func (f *Frame) setMessageType() {
|
||||||
replay := make([]*Frame, frameCount)
|
|
||||||
|
|
||||||
// Decode all frames and add them to *Frame slice
|
switch f.Kind & ^demo.EDemoCommands_DEM_IsCompressed {
|
||||||
for i := 0; i < int(frameCount); i++ {
|
default:
|
||||||
replay[i], _ = DecodeNextFrame(r)
|
f.Message = nil
|
||||||
|
// case demo.EDemoCommands_DEM_Error:
|
||||||
|
// f.Message = &demo.CDemoError{}
|
||||||
|
case demo.EDemoCommands_DEM_Stop:
|
||||||
|
f.Message = &demo.CDemoStop{}
|
||||||
|
case demo.EDemoCommands_DEM_FileHeader:
|
||||||
|
f.Message = &demo.CDemoFileHeader{}
|
||||||
|
case demo.EDemoCommands_DEM_FileInfo:
|
||||||
|
f.Message = &demo.CDemoFileInfo{}
|
||||||
|
case demo.EDemoCommands_DEM_SyncTick:
|
||||||
|
f.Message = &demo.CDemoSyncTick{}
|
||||||
|
case demo.EDemoCommands_DEM_SendTables:
|
||||||
|
f.Message = &demo.CDemoSendTables{}
|
||||||
|
f.HasEmbeddedData = true
|
||||||
|
case demo.EDemoCommands_DEM_ClassInfo:
|
||||||
|
f.Message = &demo.CDemoClassInfo{}
|
||||||
|
case demo.EDemoCommands_DEM_StringTables:
|
||||||
|
f.Message = &demo.CDemoStringTables{}
|
||||||
|
case demo.EDemoCommands_DEM_Packet:
|
||||||
|
f.Message = &demo.CDemoPacket{}
|
||||||
|
f.HasEmbeddedData = true
|
||||||
|
case demo.EDemoCommands_DEM_SignonPacket:
|
||||||
|
f.Message = nil //&demo.CDemoSignonPacket{}
|
||||||
|
f.HasEmbeddedData = true
|
||||||
|
case demo.EDemoCommands_DEM_ConsoleCmd:
|
||||||
|
f.Message = &demo.CDemoConsoleCmd{}
|
||||||
|
case demo.EDemoCommands_DEM_CustomData:
|
||||||
|
f.Message = &demo.CDemoCustomData{}
|
||||||
|
case demo.EDemoCommands_DEM_CustomDataCallbacks:
|
||||||
|
f.Message = &demo.CDemoCustomDataCallbacks{}
|
||||||
|
case demo.EDemoCommands_DEM_UserCmd:
|
||||||
|
f.Message = &demo.CDemoUserCmd{}
|
||||||
|
case demo.EDemoCommands_DEM_FullPacket:
|
||||||
|
f.Message = &demo.CDemoFullPacket{}
|
||||||
|
f.HasEmbeddedData = true
|
||||||
|
case demo.EDemoCommands_DEM_SaveGame:
|
||||||
|
f.Message = &demo.CDemoSaveGame{}
|
||||||
|
case demo.EDemoCommands_DEM_SpawnGroups:
|
||||||
|
f.Message = &demo.CDemoSpawnGroups{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return replay, nil
|
}
|
||||||
|
|
||||||
|
// Following code is stolen from binary package
|
||||||
|
const MaxVarintLen32 = 5
|
||||||
|
|
||||||
|
func ReadUvarint32(r io.ByteReader) (uint32, error) {
|
||||||
|
var x uint32
|
||||||
|
var s uint
|
||||||
|
for i := 0; i < MaxVarintLen32; i++ {
|
||||||
|
b, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return x, err
|
||||||
|
}
|
||||||
|
if b < 0x80 {
|
||||||
|
if i == MaxVarintLen32-1 && b > 1 {
|
||||||
|
return x, errors.New("binary: varint overflows a 32-bit integer")
|
||||||
|
}
|
||||||
|
return x | uint32(b)<<s, nil
|
||||||
|
}
|
||||||
|
x |= uint32(b&0x7f) << s
|
||||||
|
s += 7
|
||||||
|
}
|
||||||
|
return x, errors.New("binary: varint overflows a 32-bit integer")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user