diff options
| author | Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> | 2024-06-06 22:24:34 +0300 |
|---|---|---|
| committer | Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> | 2024-06-06 22:24:34 +0300 |
| commit | eead3c2bd90fa428dca616bc56fcd85f362f4040 (patch) | |
| tree | adcf19686e86c2ce91cff67190d258c186e2bd5b | |
| parent | change project name, update to go1.22 (diff) | |
| download | sdp.go-eead3c2bd90fa428dca616bc56fcd85f362f4040.tar.gz sdp.go-eead3c2bd90fa428dca616bc56fcd85f362f4040.tar.bz2 sdp.go-eead3c2bd90fa428dca616bc56fcd85f362f4040.zip | |
change project name, decouple main and parsing entrance
this will make it more like a library, where another project can create Parser struct and call ParseDemos to get all relevant information, and if anyone wants to use executable from this project, the main function works exactly the same
| -rw-r--r-- | cmd/parser.go | 107 | ||||
| -rw-r--r-- | pkg/packets/packets.go | 104 |
2 files changed, 151 insertions, 60 deletions
diff --git a/cmd/parser.go b/cmd/parser.go index b4cbba4..f21d91f 100644 --- a/cmd/parser.go +++ b/cmd/parser.go | |||
| @@ -1,53 +1,106 @@ | |||
| 1 | package main | 1 | package main |
| 2 | 2 | ||
| 3 | import ( | 3 | import ( |
| 4 | "errors" | ||
| 4 | "fmt" | 5 | "fmt" |
| 5 | "os" | 6 | "os" |
| 7 | "strings" | ||
| 6 | 8 | ||
| 7 | "github.com/pektezol/bitreader" | 9 | "github.com/pektezol/bitreader" |
| 8 | "github.com/pektezol/demoparser/pkg/packets" | 10 | "github.com/pektezol/sdp.go/pkg/packets" |
| 9 | "github.com/pektezol/demoparser/pkg/writer" | 11 | "github.com/pektezol/sdp.go/pkg/writer" |
| 10 | ) | 12 | ) |
| 11 | 13 | ||
| 14 | type Demo struct { | ||
| 15 | Headers packets.Headers `json:"headers"` | ||
| 16 | Messages []packets.Message `json:"messages"` | ||
| 17 | } | ||
| 18 | |||
| 19 | type Parser struct { | ||
| 20 | DemoPath string | ||
| 21 | writer strings.Builder | ||
| 22 | } | ||
| 23 | |||
| 24 | func NewParser(demoPath string) *Parser { | ||
| 25 | return &Parser{ | ||
| 26 | DemoPath: demoPath, | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 12 | const littleEndian bool = true | 30 | const littleEndian bool = true |
| 13 | 31 | ||
| 14 | func main() { | 32 | func main() { |
| 15 | if len(os.Args) != 2 { | 33 | if len(os.Args) != 2 { |
| 16 | panic("specify file in command line arguments") | 34 | fmt.Println("specify file in command line arguments") |
| 35 | os.Exit(1) | ||
| 17 | } | 36 | } |
| 18 | writer.AppendLine("Generated By: github.com/pektezol/demoparser") | 37 | parser := NewParser(os.Args[1]) |
| 19 | files, err := os.ReadDir(os.Args[1]) | 38 | _, err := parser.ParseDemos() |
| 20 | if err != nil { // If it's not a directory | 39 | if err != nil { |
| 21 | file, err := os.Open(os.Args[1]) | 40 | fmt.Println(err.Error()) |
| 22 | if err != nil { | 41 | os.Exit(1) |
| 23 | panic(err) | ||
| 24 | } | ||
| 25 | writer.AppendLine("\nFile Name: %s", file.Name()) | ||
| 26 | reader := bitreader.NewReader(file, littleEndian) | ||
| 27 | demoParserHandler(reader) | ||
| 28 | defer file.Close() | ||
| 29 | fmt.Println(writer.GetString()) | ||
| 30 | return | ||
| 31 | } | 42 | } |
| 32 | for _, fileinfo := range files { // If it is a directory | 43 | fmt.Println(parser.GetOutput()) |
| 33 | file, err := os.Open(os.Args[1] + fileinfo.Name()) | 44 | } |
| 45 | |||
| 46 | func (p *Parser) GetOutput() string { | ||
| 47 | return p.writer.String() | ||
| 48 | } | ||
| 49 | |||
| 50 | func (p *Parser) ParseDemos() ([]Demo, error) { | ||
| 51 | writer.AppendLine("Generated By: github.com/pektezol/sdp.go") | ||
| 52 | files, err := os.ReadDir(p.DemoPath) | ||
| 53 | if err != nil { | ||
| 54 | // not a directory | ||
| 55 | file, err := os.Open(p.DemoPath) | ||
| 34 | if err != nil { | 56 | if err != nil { |
| 35 | panic(err) | 57 | return []Demo{}, err |
| 36 | } | 58 | } |
| 37 | writer.AppendLine("\nFile Name: %s", file.Name()) | ||
| 38 | reader := bitreader.NewReader(file, littleEndian) | ||
| 39 | demoParserHandler(reader) | ||
| 40 | defer file.Close() | 59 | defer file.Close() |
| 60 | reader := bitreader.NewReader(file, littleEndian) | ||
| 61 | demo := demoParserHandler(reader, file.Name()) | ||
| 62 | p.writer = writer.GetWriter() | ||
| 63 | return []Demo{demo}, nil | ||
| 64 | } else { | ||
| 65 | demos := []Demo{} | ||
| 66 | // directory | ||
| 67 | for _, fileinfo := range files { | ||
| 68 | if len(fileinfo.Name()) > 4 && fileinfo.Name()[len(fileinfo.Name())-4:] == ".dem" { | ||
| 69 | file, err := os.Open(p.DemoPath + fileinfo.Name()) | ||
| 70 | if err != nil { | ||
| 71 | return []Demo{}, err | ||
| 72 | } | ||
| 73 | defer file.Close() | ||
| 74 | reader := bitreader.NewReader(file, littleEndian) | ||
| 75 | demo := demoParserHandler(reader, file.Name()) | ||
| 76 | demos = append(demos, demo) | ||
| 77 | } | ||
| 78 | } | ||
| 79 | p.writer = writer.GetWriter() | ||
| 80 | if len(demos) == 0 { | ||
| 81 | return demos, errors.New("no demo found in given directory") | ||
| 82 | } | ||
| 83 | return demos, nil | ||
| 41 | } | 84 | } |
| 42 | fmt.Println(writer.GetString()) | ||
| 43 | } | 85 | } |
| 44 | 86 | ||
| 45 | func demoParserHandler(reader *bitreader.Reader) { | 87 | func demoParserHandler(reader *bitreader.Reader, filename string) Demo { |
| 46 | packets.ParseHeaders(reader) | 88 | demo := Demo{} |
| 89 | writer.AppendLine("\nFile Name: %s", filename) | ||
| 90 | // this is for recovering after a panic inside parse headers and packet. | ||
| 91 | // this approach was taken since error handling bitreader functions would take a long time. | ||
| 92 | defer func() { | ||
| 93 | if err := recover(); err != nil { | ||
| 94 | writer.AppendLine("failed to parse demo: %v", err) | ||
| 95 | } | ||
| 96 | }() | ||
| 97 | demo.Headers = packets.ParseHeaders(reader) | ||
| 47 | for { | 98 | for { |
| 48 | packet := packets.ParsePackets(reader) | 99 | message := packets.ParseMessage(reader) |
| 49 | if packet.PacketType == 7 { | 100 | demo.Messages = append(demo.Messages, message) |
| 101 | if message.PacketType == 7 { | ||
| 50 | break | 102 | break |
| 51 | } | 103 | } |
| 52 | } | 104 | } |
| 105 | return demo | ||
| 53 | } | 106 | } |
diff --git a/pkg/packets/packets.go b/pkg/packets/packets.go index b1d6a40..1bf3f57 100644 --- a/pkg/packets/packets.go +++ b/pkg/packets/packets.go | |||
| @@ -2,63 +2,101 @@ package packets | |||
| 2 | 2 | ||
| 3 | import ( | 3 | import ( |
| 4 | "github.com/pektezol/bitreader" | 4 | "github.com/pektezol/bitreader" |
| 5 | "github.com/pektezol/demoparser/pkg/classes" | 5 | "github.com/pektezol/sdp.go/pkg/classes" |
| 6 | "github.com/pektezol/demoparser/pkg/writer" | 6 | "github.com/pektezol/sdp.go/pkg/writer" |
| 7 | ) | 7 | ) |
| 8 | 8 | ||
| 9 | type PacketMessageInfo struct { | 9 | type MessageType uint8 |
| 10 | PacketType uint8 | 10 | |
| 11 | const ( | ||
| 12 | SignOn MessageType = iota + 1 | ||
| 13 | Packet | ||
| 14 | SyncTick | ||
| 15 | ConsoleCmd | ||
| 16 | UserCmd | ||
| 17 | DataTables | ||
| 18 | Stop | ||
| 19 | CustomData | ||
| 20 | StringTables | ||
| 21 | ) | ||
| 22 | |||
| 23 | type Message struct { | ||
| 24 | PacketType MessageType | ||
| 11 | TickNumber int32 | 25 | TickNumber int32 |
| 12 | SlotNumber uint8 | 26 | SlotNumber uint8 |
| 27 | Data any | ||
| 13 | } | 28 | } |
| 14 | 29 | ||
| 15 | func ParsePackets(reader *bitreader.Reader) PacketMessageInfo { | 30 | func ParseMessage(reader *bitreader.Reader) Message { |
| 16 | packetType := reader.TryReadUInt8() | 31 | message := Message{ |
| 17 | tickNumber := reader.TryReadSInt32() | 32 | PacketType: MessageType(reader.TryReadUInt8()), |
| 18 | slotNumber := reader.TryReadUInt8() | 33 | TickNumber: reader.TryReadSInt32(), |
| 19 | switch packetType { | 34 | SlotNumber: reader.TryReadUInt8(), |
| 20 | case 1: // SignOn | 35 | } |
| 21 | writer.AppendLine("[%d] %s (%d):", tickNumber, "SIGNON", packetType) | 36 | writer.AppendLine("[%d] %s (%d):", message.TickNumber, message.PacketType.String(), message.PacketType) |
| 37 | switch message.PacketType { | ||
| 38 | case SignOn: | ||
| 22 | signOn := classes.SignOn{} | 39 | signOn := classes.SignOn{} |
| 23 | signOn.ParseSignOn(reader) | 40 | signOn.ParseSignOn(reader) |
| 24 | case 2: // Packet | 41 | message.Data = signOn |
| 25 | writer.AppendLine("[%d] %s (%d):", tickNumber, "PACKET", packetType) | 42 | case Packet: |
| 26 | packet := classes.Packet{} | 43 | packet := classes.Packet{} |
| 27 | packet.ParsePacket(reader) | 44 | packet.ParsePacket(reader) |
| 28 | case 3: // SyncTick | 45 | message.Data = packet |
| 29 | writer.AppendLine("[%d] %s (%d):", tickNumber, "SYNCTICK", packetType) | 46 | case SyncTick: |
| 30 | syncTick := classes.SyncTick{} | 47 | syncTick := classes.SyncTick{} |
| 31 | syncTick.ParseSyncTick() | 48 | syncTick.ParseSyncTick() |
| 32 | case 4: // ConsoleCmd | 49 | message.Data = syncTick |
| 33 | writer.AppendLine("[%d] %s (%d):", tickNumber, "CONSOLECMD", packetType) | 50 | case ConsoleCmd: |
| 34 | consoleCmd := classes.ConsoleCmd{} | 51 | consoleCmd := classes.ConsoleCmd{} |
| 35 | consoleCmd.ParseConsoleCmd(reader) | 52 | consoleCmd.ParseConsoleCmd(reader) |
| 36 | case 5: // UserCmd | 53 | message.Data = consoleCmd |
| 37 | writer.AppendLine("[%d] %s (%d):", tickNumber, "USERCMD", packetType) | 54 | case UserCmd: |
| 38 | userCmd := classes.UserCmd{} | 55 | userCmd := classes.UserCmd{} |
| 39 | userCmd.ParseUserCmd(reader) | 56 | userCmd.ParseUserCmd(reader) |
| 40 | case 6: // DataTables | 57 | message.Data = userCmd |
| 41 | writer.AppendLine("[%d] %s (%d):", tickNumber, "DATATABLES", packetType) | 58 | case DataTables: |
| 42 | dataTables := classes.DataTables{} | 59 | dataTables := classes.DataTables{} |
| 43 | dataTables.ParseDataTables(reader) | 60 | dataTables.ParseDataTables(reader) |
| 44 | case 7: // Stop | 61 | message.Data = dataTables |
| 45 | writer.AppendLine("[%d] %s (%d):", tickNumber, "STOP", packetType) | 62 | case Stop: |
| 46 | stop := classes.Stop{} | 63 | stop := classes.Stop{} |
| 47 | stop.ParseStop(reader) | 64 | stop.ParseStop(reader) |
| 48 | case 8: // CustomData TODO: not sar data | 65 | message.Data = stop |
| 66 | case CustomData: // TODO: not sar data | ||
| 49 | customData := classes.CustomData{} | 67 | customData := classes.CustomData{} |
| 50 | customData.ParseCustomData(reader, tickNumber, packetType) | 68 | customData.ParseCustomData(reader, message.TickNumber, uint8(message.PacketType)) |
| 51 | case 9: // StringTables TODO: parsing string table data | 69 | message.Data = customData |
| 52 | writer.AppendLine("[%d] %s (%d):", tickNumber, "STRINGTABLES", packetType) | 70 | case StringTables: // TODO: parsing string table data |
| 53 | stringTables := classes.StringTables{} | 71 | stringTables := classes.StringTables{} |
| 54 | stringTables.ParseStringTables(reader) | 72 | stringTables.ParseStringTables(reader) |
| 55 | default: // Invalid | 73 | message.Data = stringTables |
| 56 | writer.AppendLine("[%d] %s (%d):", tickNumber, "INVALID", packetType) | 74 | default: |
| 57 | panic("invalid packet type") | 75 | panic("invalid packet type") |
| 58 | } | 76 | } |
| 59 | return PacketMessageInfo{ | 77 | return message |
| 60 | PacketType: packetType, | 78 | } |
| 61 | TickNumber: tickNumber, | 79 | |
| 62 | SlotNumber: slotNumber, | 80 | func (t MessageType) String() string { |
| 81 | switch t { | ||
| 82 | case SignOn: | ||
| 83 | return "SIGNON" | ||
| 84 | case Packet: | ||
| 85 | return "PACKET" | ||
| 86 | case SyncTick: | ||
| 87 | return "SYNCTICK" | ||
| 88 | case ConsoleCmd: | ||
| 89 | return "CONSOLECMD" | ||
| 90 | case UserCmd: | ||
| 91 | return "USERCMD" | ||
| 92 | case DataTables: | ||
| 93 | return "DATATABLES" | ||
| 94 | case Stop: | ||
| 95 | return "STOP" | ||
| 96 | case CustomData: | ||
| 97 | return "CUSTOMDATA" | ||
| 98 | case StringTables: | ||
| 99 | return "STRINGTABLES" | ||
| 63 | } | 100 | } |
| 101 | return "INVALID" | ||
| 64 | } | 102 | } |