first commit
This commit is contained in:
2032
demo/demo.pb.go
Normal file
2032
demo/demo.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
8
go.mod
Normal file
8
go.mod
Normal file
@@ -0,0 +1,8 @@
|
||||
module github.com/olemorud/replay-parser
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/golang/snappy v0.0.4
|
||||
google.golang.org/protobuf v1.27.1
|
||||
)
|
||||
10
go.sum
Normal file
10
go.sum
Normal file
@@ -0,0 +1,10 @@
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
45
main.go
Normal file
45
main.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/olemorud/replay-parser/parse"
|
||||
)
|
||||
|
||||
func main() {
|
||||
f, err := os.Open("replays/dog2.dem")
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Errorf("error reading file: %v", err))
|
||||
}
|
||||
|
||||
r := bufio.NewReader(f)
|
||||
|
||||
size, err := parse.First(r)
|
||||
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)
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Errorf("error parsing frame: %v", err))
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v", frame.Message)
|
||||
|
||||
for _, player := range frame.Message.GameInfo.Dota.PlayerInfo {
|
||||
fmt.Println(*player.HeroName)
|
||||
fmt.Println(*player.PlayerName)
|
||||
fmt.Println("Steam:", *player.Steamid)
|
||||
fmt.Println("")
|
||||
|
||||
}
|
||||
|
||||
//fmt.Printf("%+v", )
|
||||
|
||||
}
|
||||
3
parse/const.go
Normal file
3
parse/const.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package parse
|
||||
|
||||
const SIGNATURE = "PBDEMS2\x00"
|
||||
98
parse/parse.go
Normal file
98
parse/parse.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package parse
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/olemorud/replay-parser/demo"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type Frame struct {
|
||||
Kind uint64 `json:"kind"` // see demo.proto
|
||||
Tick uint64 `json:"tick"` // time elapsed in replay time
|
||||
Size uint64 `json:"size"` // size of message in bytes
|
||||
Message *demo.CDemoFileInfo `json:"message"` // protobuf encoded message (may be compressed with snappy)
|
||||
}
|
||||
|
||||
// If kind has a certain bit set, the frame message is compressed
|
||||
// returns true if frame is compressed
|
||||
func isCompressed(frame *Frame) bool {
|
||||
return (frame.Kind&uint64(demo.EDemoCommands_DEM_IsCompressed) != 0)
|
||||
}
|
||||
|
||||
// Checks if file header is correct and returns number the address of the last frame
|
||||
func First(r *bufio.Reader) (uint64, error) {
|
||||
// Check if first 8 bytes of file matches the source 2 replay file header
|
||||
header := make([]byte, 8)
|
||||
if _, err := io.ReadFull(r, header); err != nil {
|
||||
return 0, fmt.Errorf("error when reading file: %v", err)
|
||||
}
|
||||
if string(header) != SIGNATURE {
|
||||
return 0, fmt.Errorf("wrong file signature: %v, should be ", SIGNATURE)
|
||||
}
|
||||
|
||||
// Read last frame
|
||||
var offset uint32
|
||||
err := binary.Read(r, binary.LittleEndian, &offset)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// Parses the next frame on the reader
|
||||
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 {
|
||||
return nil, fmt.Errorf("error reading frame kind for frame: %v", err)
|
||||
}
|
||||
|
||||
if frame.Tick, err = binary.ReadUvarint(r); err != nil {
|
||||
return nil, fmt.Errorf("error reading frame tick for frame: %v", err)
|
||||
}
|
||||
|
||||
if frame.Size, err = binary.ReadUvarint(r); err != nil {
|
||||
return nil, fmt.Errorf("error reading frame size for frame: %v", err)
|
||||
}
|
||||
|
||||
message := make([]byte, frame.Size)
|
||||
io.ReadFull(r, message)
|
||||
|
||||
frame.Message = new(demo.CDemoFileInfo)
|
||||
|
||||
if isCompressed(frame) {
|
||||
decoded, err := snappy.Decode(nil, message)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding message: %v", err)
|
||||
}
|
||||
fmt.Println("I got decompressed :O")
|
||||
proto.Unmarshal(decoded, frame.Message)
|
||||
} else {
|
||||
proto.Unmarshal(message, frame.Message)
|
||||
}
|
||||
|
||||
return frame, nil
|
||||
}
|
||||
|
||||
// Reads every single frame (probably not very efficient)
|
||||
func DecodeAllFrames(r *bufio.Reader, frameCount uint64) ([]*Frame, error) {
|
||||
replay := make([]*Frame, frameCount)
|
||||
|
||||
// Decode all frames and add them to *Frame slice
|
||||
for i := 0; i < int(frameCount); i++ {
|
||||
replay[i], _ = DecodeNextFrame(r)
|
||||
}
|
||||
|
||||
return replay, nil
|
||||
}
|
||||
155
proto/demo.proto
Normal file
155
proto/demo.proto
Normal file
@@ -0,0 +1,155 @@
|
||||
syntax = "proto2";
|
||||
|
||||
option go_package = "./demo";
|
||||
option cc_generic_services = false;
|
||||
|
||||
enum EDemoCommands {
|
||||
DEM_Error = -1;
|
||||
DEM_Stop = 0;
|
||||
DEM_FileHeader = 1;
|
||||
DEM_FileInfo = 2;
|
||||
DEM_SyncTick = 3;
|
||||
DEM_SendTables = 4;
|
||||
DEM_ClassInfo = 5;
|
||||
DEM_StringTables = 6;
|
||||
DEM_Packet = 7;
|
||||
DEM_SignonPacket = 8;
|
||||
DEM_ConsoleCmd = 9;
|
||||
DEM_CustomData = 10;
|
||||
DEM_CustomDataCallbacks = 11;
|
||||
DEM_UserCmd = 12;
|
||||
DEM_FullPacket = 13;
|
||||
DEM_SaveGame = 14;
|
||||
DEM_SpawnGroups = 15;
|
||||
DEM_Max = 16;
|
||||
DEM_IsCompressed = 64;
|
||||
}
|
||||
|
||||
message CDemoFileHeader {
|
||||
required string demo_file_stamp = 1;
|
||||
optional int32 network_protocol = 2;
|
||||
optional string server_name = 3;
|
||||
optional string client_name = 4;
|
||||
optional string map_name = 5;
|
||||
optional string game_directory = 6;
|
||||
optional int32 fullpackets_version = 7;
|
||||
optional bool allow_clientside_entities = 8;
|
||||
optional bool allow_clientside_particles = 9;
|
||||
optional string addons = 10;
|
||||
optional string demo_version_name = 11;
|
||||
optional string demo_version_guid = 12;
|
||||
optional int32 build_num = 13;
|
||||
}
|
||||
|
||||
message CGameInfo {
|
||||
message CDotaGameInfo {
|
||||
message CPlayerInfo {
|
||||
optional string hero_name = 1;
|
||||
optional string player_name = 2;
|
||||
optional bool is_fake_client = 3;
|
||||
optional uint64 steamid = 4;
|
||||
optional int32 game_team = 5;
|
||||
}
|
||||
|
||||
message CHeroSelectEvent {
|
||||
optional bool is_pick = 1;
|
||||
optional uint32 team = 2;
|
||||
optional uint32 hero_id = 3;
|
||||
}
|
||||
|
||||
optional uint64 match_id = 1;
|
||||
optional int32 game_mode = 2;
|
||||
optional int32 game_winner = 3;
|
||||
repeated .CGameInfo.CDotaGameInfo.CPlayerInfo player_info = 4;
|
||||
optional uint32 leagueid = 5;
|
||||
repeated .CGameInfo.CDotaGameInfo.CHeroSelectEvent picks_bans = 6;
|
||||
optional uint32 radiant_team_id = 7;
|
||||
optional uint32 dire_team_id = 8;
|
||||
optional string radiant_team_tag = 9;
|
||||
optional string dire_team_tag = 10;
|
||||
optional uint32 end_time = 11;
|
||||
}
|
||||
|
||||
optional .CGameInfo.CDotaGameInfo dota = 4;
|
||||
}
|
||||
|
||||
message CDemoFileInfo {
|
||||
optional float playback_time = 1;
|
||||
optional int32 playback_ticks = 2;
|
||||
optional int32 playback_frames = 3;
|
||||
optional .CGameInfo game_info = 4;
|
||||
}
|
||||
|
||||
message CDemoPacket {
|
||||
optional bytes data = 3;
|
||||
}
|
||||
|
||||
message CDemoFullPacket {
|
||||
optional .CDemoStringTables string_table = 1;
|
||||
optional .CDemoPacket packet = 2;
|
||||
}
|
||||
|
||||
message CDemoSaveGame {
|
||||
optional bytes data = 1;
|
||||
optional fixed64 steam_id = 2;
|
||||
optional fixed64 signature = 3;
|
||||
optional int32 version = 4;
|
||||
}
|
||||
|
||||
message CDemoSyncTick {
|
||||
}
|
||||
|
||||
message CDemoConsoleCmd {
|
||||
optional string cmdstring = 1;
|
||||
}
|
||||
|
||||
message CDemoSendTables {
|
||||
optional bytes data = 1;
|
||||
}
|
||||
|
||||
message CDemoClassInfo {
|
||||
message class_t {
|
||||
optional int32 class_id = 1;
|
||||
optional string network_name = 2;
|
||||
optional string table_name = 3;
|
||||
}
|
||||
|
||||
repeated .CDemoClassInfo.class_t classes = 1;
|
||||
}
|
||||
|
||||
message CDemoCustomData {
|
||||
optional int32 callback_index = 1;
|
||||
optional bytes data = 2;
|
||||
}
|
||||
|
||||
message CDemoCustomDataCallbacks {
|
||||
repeated string save_id = 1;
|
||||
}
|
||||
|
||||
message CDemoStringTables {
|
||||
message items_t {
|
||||
optional string str = 1;
|
||||
optional bytes data = 2;
|
||||
}
|
||||
|
||||
message table_t {
|
||||
optional string table_name = 1;
|
||||
repeated .CDemoStringTables.items_t items = 2;
|
||||
repeated .CDemoStringTables.items_t items_clientside = 3;
|
||||
optional int32 table_flags = 4;
|
||||
}
|
||||
|
||||
repeated .CDemoStringTables.table_t tables = 1;
|
||||
}
|
||||
|
||||
message CDemoStop {
|
||||
}
|
||||
|
||||
message CDemoUserCmd {
|
||||
optional int32 cmd_number = 1;
|
||||
optional bytes data = 2;
|
||||
}
|
||||
|
||||
message CDemoSpawnGroups {
|
||||
repeated bytes msgs = 3;
|
||||
}
|
||||
BIN
replays/dog.dem
Normal file
BIN
replays/dog.dem
Normal file
Binary file not shown.
BIN
replays/dog2.dem
Normal file
BIN
replays/dog2.dem
Normal file
Binary file not shown.
Reference in New Issue
Block a user