From 18dc647ec85e77aca0a01fd701bf0dc115096426 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 6 Nov 2023 18:43:54 +0300 Subject: feat: inline parser instead of executable (#113) Former-commit-id: 5fc9bc33b92dc674c23e3fe4dc4e852615ffad24 --- backend/parser/parser-arm64.REMOVED.git-id | 1 - backend/parser/parser.go | 249 ++++++++++++++++++++++++++--- 2 files changed, 223 insertions(+), 27 deletions(-) delete mode 100644 backend/parser/parser-arm64.REMOVED.git-id (limited to 'backend') diff --git a/backend/parser/parser-arm64.REMOVED.git-id b/backend/parser/parser-arm64.REMOVED.git-id deleted file mode 100644 index 1491e2b..0000000 --- a/backend/parser/parser-arm64.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -2dd9762c1d6c97b34a1a88b7f788c6fc58ad4b47 \ No newline at end of file diff --git a/backend/parser/parser.go b/backend/parser/parser.go index bf456d4..db84256 100644 --- a/backend/parser/parser.go +++ b/backend/parser/parser.go @@ -1,40 +1,237 @@ package parser import ( - "bufio" - "os/exec" - "strconv" - "strings" + "errors" + "math" + "os" + + "github.com/pektezol/bitreader" ) -func ProcessDemo(demoPath string) (int, int, error) { - cmd := exec.Command("./backend/parser/parser-arm64", demoPath) - stdout, err := cmd.StdoutPipe() +// Don't try to understand it, feel it. +func ProcessDemo(filePath string) (portalCount int, tickCount int, err error) { + file, err := os.Open(filePath) if err != nil { return 0, 0, err } - if err := cmd.Start(); err != nil { - return 0, 0, err + reader := bitreader.NewReader(file, true) + demoFileStamp := reader.TryReadString() + demoProtocol := reader.TryReadSInt32() + networkProtocol := reader.TryReadSInt32() + reader.SkipBytes(1056) + if demoFileStamp != "HL2DEMO" { + return 0, 0, errors.New("invalid demo file stamp") + } + if demoProtocol != 4 { + return 0, 0, errors.New("this parser only supports demos from new engine") } - scanner := bufio.NewScanner(stdout) - var cmTicks, portalCount int - for scanner.Scan() { - line := scanner.Text() - if strings.Contains(line, "CM Ticks") { - cmTicksStr := strings.TrimSpace(strings.Split(line, ":")[1]) - cmTicks, err = strconv.Atoi(cmTicksStr) - if err != nil { - return 0, 0, err + if networkProtocol != 2001 { + return 0, 0, errors.New("this parser only supports demos from portal 2") + } + for { + packetType := reader.TryReadUInt8() + reader.SkipBits(40) + switch packetType { + case 1: + reader.SkipBytes(160) + reader.SkipBytes(uint64(reader.TryReadSInt32())) + case 2: + reader.SkipBytes(160) + size := reader.TryReadUInt32() + packetReader := bitreader.NewReaderFromBytes(reader.TryReadBytesToSlice(uint64(size)), true) + for { + messageType, err := packetReader.ReadBits(6) + if err != nil { + break + } + switch messageType { + case 0: + case 1: + packetReader.TryReadString() + case 2: + packetReader.SkipBytes(4) + packetReader.TryReadString() + packetReader.SkipBits(2) + case 3: + packetReader.SkipBits(1) + case 4: + packetReader.SkipBytes(8) + case 5: + packetReader.TryReadString() + case 6: + for count := 0; count < int(packetReader.TryReadUInt8()); count++ { + packetReader.TryReadString() + packetReader.TryReadString() + } + case 7: + packetReader.SkipBytes(9) + idsLength := packetReader.TryReadUInt32() + if idsLength > 0 { + packetReader.SkipBytes(uint64(idsLength)) + } + mapLength := packetReader.TryReadUInt32() + if mapLength > 0 { + packetReader.TryReadStringLength(uint64(mapLength)) + } + case 8: + packetReader.SkipBits(210) + packetReader.TryReadStringLength(1) + packetReader.TryReadString() + packetReader.TryReadString() + packetReader.TryReadString() + packetReader.TryReadString() + case 9: + packetReader.SkipBits(1) + packetReader.SkipBits(uint64(packetReader.TryReadUInt8())) + case 10: + classCount := packetReader.TryReadUInt16() + if !packetReader.TryReadBool() { + for count := 0; count < int(classCount); count++ { + packetReader.SkipBits(uint64(math.Log2(float64(classCount)) + 1)) + packetReader.TryReadString() + packetReader.TryReadString() + } + } + case 11: + packetReader.SkipBits(1) + case 12: + packetReader.TryReadString() + maxEntries := packetReader.TryReadSInt16() + packetReader.SkipBits(uint64(math.Log2(float64(maxEntries))) + 1) + length := packetReader.TryReadBits(20) + if packetReader.TryReadBool() { + packetReader.SkipBytes(2) + } + packetReader.SkipBits(2) + packetReader.SkipBits(length) + case 13: + packetReader.SkipBits(5) + if packetReader.TryReadBool() { + packetReader.SkipBytes(2) + } + packetReader.SkipBits(packetReader.TryReadBits(20)) + case 14: + packetReader.TryReadString() + if packetReader.TryReadUInt8() == 255 { + packetReader.SkipBytes(4) + } + case 15: + packetReader.SkipBytes(2) + packetReader.SkipBits(uint64(packetReader.TryReadUInt16())) + case 16: + packetReader.TryReadString() + case 17: + var length uint16 + if packetReader.TryReadBool() { + length = uint16(packetReader.TryReadUInt8()) + } else { + packetReader.SkipBytes(1) + length = packetReader.TryReadUInt16() + } + packetReader.SkipBits(uint64(length)) + case 18: + packetReader.SkipBits(11) + case 19: + packetReader.SkipBits(49) + case 20: + packetReader.SkipBits(48) + case 21: + readVectorCoord := func() float32 { + value := float32(0) + integer := packetReader.TryReadBits(1) + fraction := packetReader.TryReadBits(1) + if integer != 0 || fraction != 0 { + sign := packetReader.TryReadBits(1) + if integer != 0 { + integer = packetReader.TryReadBits(uint64(14)) + 1 + } + if fraction != 0 { + fraction = packetReader.TryReadBits(uint64(5)) + } + value = float32(integer) + float32(fraction)*(1.0/float32(1<<5)) + if sign != 0 { + value = -value + } + } + return value + } + packetReader.SkipBits(3) + readVectorCoord() + readVectorCoord() + readVectorCoord() + packetReader.SkipBits(9) + if packetReader.TryReadBool() { + packetReader.SkipBits(22) + } + packetReader.SkipBits(1) + case 22: + packetReader.SkipBits(1) + packetReader.SkipBits(packetReader.TryReadBits(11)) + case 23: + msgType := int8(packetReader.TryReadBits(8)) + msgLength := packetReader.TryReadBits(12) + userMessageReader := bitreader.NewReaderFromBytes(packetReader.TryReadBitsToSlice(msgLength), true) + switch msgType { + case 60: + scoreboardTempUpdate := struct { + NumPortals int32 + TimeTaken int32 + }{ + NumPortals: userMessageReader.TryReadSInt32(), + TimeTaken: userMessageReader.TryReadSInt32(), + } + portalCount = int(scoreboardTempUpdate.NumPortals) + tickCount = int(math.Round(float64((float32(scoreboardTempUpdate.TimeTaken) / 100.0) / float32(1.0/60.0)))) + } + case 24: + packetReader.SkipBits(20) + packetReader.SkipBits(packetReader.TryReadBits(11)) + case 25: + packetReader.SkipBits(packetReader.TryReadBits(11)) + case 26: + packetReader.SkipBits(11) + if packetReader.TryReadBool() { + packetReader.SkipBytes(4) + } + packetReader.SkipBits(12) + length := packetReader.TryReadBits(20) + packetReader.SkipBits(1) + packetReader.SkipBits(length) + + case 27: + packetReader.SkipBits(8) + packetReader.SkipBits(packetReader.TryReadBits(17)) + case 28: + packetReader.SkipBits(13) + case 29: + packetReader.SkipBits(16) + packetReader.SkipBits(packetReader.TryReadBits(32)) + case 30: + packetReader.SkipBits(9) + packetReader.SkipBits(packetReader.TryReadBits(20)) + case 31: + packetReader.SkipBits(32) + packetReader.TryReadString() + case 32: + packetReader.SkipBytes(packetReader.TryReadBits(32)) + case 33: + packetReader.SkipBits(packetReader.TryReadBits(32)) + default: + panic("unknown msg type") + } } + case 3, 7: + case 4, 6, 9: + reader.SkipBytes(uint64(reader.TryReadSInt32())) + case 5, 8: + reader.SkipBits(32) + reader.SkipBytes(uint64(reader.TryReadSInt32())) + default: + return 0, 0, errors.New("invalid packet type") } - if strings.Contains(line, "Portal Count") { - portalCountStr := strings.TrimSpace(strings.Split(line, ":")[1]) - portalCount, err = strconv.Atoi(portalCountStr) - if err != nil { - return 0, 0, err - } + if packetType == 7 { + break } } - cmd.Wait() - return portalCount, cmTicks, nil + return portalCount, tickCount, nil } -- cgit v1.2.3