package main import ( "errors" "fmt" "os" "path/filepath" "sync" "github.com/pektezol/bitreader" "github.com/pektezol/sdp.go/pkg/packets" "github.com/pektezol/sdp.go/pkg/types" "github.com/pektezol/sdp.go/pkg/writer" ) type Parser struct { DemoPath string } func NewParser(demoPath string) *Parser { return &Parser{ DemoPath: demoPath, } } const littleEndian bool = true func main() { if len(os.Args) != 2 { fmt.Println("specify file in command line arguments") os.Exit(1) } parser := NewParser(os.Args[1]) demos, err := parser.ParseDemos() if err != nil { fmt.Println(err.Error()) os.Exit(1) } for _, d := range demos { fmt.Println(d.Writer.GetOutputString()) // You can also marshal structs into JSON and print them: // out, _ := json.Marshal(d) // fmt.Println(string(out)) } } func (p *Parser) ParseDemos() ([]*types.Demo, error) { files, err := os.ReadDir(p.DemoPath) if err != nil { // not a directory file, err := os.Open(p.DemoPath) if err != nil { return []*types.Demo{}, err } defer file.Close() demo := &types.Demo{} demo.Writer = writer.NewWriter() demo.Writer.AppendLine("Generated By: github.com/pektezol/sdp.go") reader := bitreader.NewReader(file, littleEndian) p.demoParserHandler(reader, file.Name(), demo, nil) return []*types.Demo{demo}, nil } else { // directory demoCount := 0 for _, fileinfo := range files { if len(fileinfo.Name()) > 4 && fileinfo.Name()[len(fileinfo.Name())-4:] == ".dem" { demoCount++ } } wg := new(sync.WaitGroup) wg.Add(demoCount) demos := make([]*types.Demo, demoCount) for i := 0; i < len(demos); i++ { demos[i] = &types.Demo{} demos[i].Writer = writer.NewWriter() demos[i].Writer.AppendLine("Generated By: github.com/pektezol/sdp.go") } for i, fileinfo := range files { if len(fileinfo.Name()) > 4 && fileinfo.Name()[len(fileinfo.Name())-4:] == ".dem" { file, err := os.Open(filepath.Join(p.DemoPath, fileinfo.Name())) if err != nil { return []*types.Demo{}, err } defer file.Close() go p.demoParserHandler(bitreader.NewReader(file, littleEndian), file.Name(), demos[i], wg) } } wg.Wait() if len(demos) == 0 { return demos, errors.New("no demo found in given directory") } return demos, nil } } func (p *Parser) demoParserHandler(reader *bitreader.Reader, filename string, demo *types.Demo, wg *sync.WaitGroup) { demo.Writer.AppendLine("\nFile Name: %s", filename) // this is for recovering after a panic inside parse headers and packet. // this approach was taken since error handling bitreader functions would take a long time. defer func() { if err := recover(); err != nil { if wg != nil { wg.Done() } demo.Writer.AppendLine("failed to parse demo: %v", err) } }() demo.Headers = packets.ParseHeaders(reader, demo) for { message := packets.ParseMessage(reader, demo) demo.Messages = append(demo.Messages, message) if message.PacketType == 7 { if wg != nil { wg.Done() } break } } }