diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 429189f..98265b0 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,12 +19,12 @@ jobs: run: | c=$(git rev-parse --short HEAD); b=$(git name-rev --name-only "$c"); echo "version=$c ($b branch)" >> $GITHUB_ENV echo "ext=$( if [ '${{ matrix.goos }}' = 'windows' ]; then echo 'dll'; elif [ '${{ matrix.goos }}' = 'darwin' ]; then echo 'dylib'; elif [ '${{ matrix.goos }}' = 'linux' ]; then echo 'so'; fi )" >> $GITHUB_ENV - - uses: wangyoucao577/go-release-action@v1.38 + - uses: wangyoucao577/go-release-action@v1.52 with: github_token: ${{ secrets.GITHUB_TOKEN }} goos: ${{ matrix.goos }} goarch: ${{ matrix.goarch }} - goversion: "1.23.0" + goversion: "1.23.3" pre_command: "go build -o libr6dissect.${{ env.ext }} -buildmode=c-shared exports/exports.go" extra_files: "LICENSE README.md libr6dissect.h libr6dissect.${{ env.ext }}" ldflags: "-X 'main.Version=${{ env.version }}'" diff --git a/dissect/header.go b/dissect/header.go index d71add9..e08f17b 100644 --- a/dissect/header.go +++ b/dissect/header.go @@ -33,11 +33,12 @@ type Header struct { } type Team struct { - Name string `json:"name"` - Score int `json:"score"` - Won bool `json:"won"` - WinCondition WinCondition `json:"winCondition,omitempty"` - Role TeamRole `json:"role,omitempty"` + Name string `json:"name"` + StartingScore int `json:"startingScore"` + Score int `json:"score"` + Won bool `json:"won"` + WinCondition WinCondition `json:"winCondition,omitempty"` + Role TeamRole `json:"role,omitempty"` } type Player struct { @@ -53,6 +54,7 @@ type Player struct { RolePortrait int `json:"rolePortrait,omitempty"` Spawn string `json:"spawn,omitempty"` DissectID []byte `json:"-" deep:"-"` // dissect player id at end of packet (4 bytes) + uiID uint64 `json:"-" deep:"-"` } type stringerIntMarshal struct { @@ -404,6 +406,8 @@ func (r *Reader) readHeader() (Header, error) { return Header{}, err } currentPlayer.RolePortrait = n + default: + props[k] = v } } _, lastProp = props["teamscore1"] @@ -502,6 +506,19 @@ func (r *Reader) readHeader() (Header, error) { return h, err } h.Teams[1].Score = n + // Parse starting team scores + if h.CodeVersion >= Y9S4 { + n, err = strconv.Atoi(props["startingteamscore0"]) + if err != nil { + return h, err + } + h.Teams[0].StartingScore = n + n, err = strconv.Atoi(props["startingteamscore1"]) + if err != nil { + return h, err + } + h.Teams[1].StartingScore = n + } return h, nil } diff --git a/dissect/player.go b/dissect/player.go index bfdb2a2..f91dee9 100644 --- a/dissect/player.go +++ b/dissect/player.go @@ -92,6 +92,20 @@ func readPlayer(r *Reader) error { if r.playersRead > 5 { teamIndex = 1 } + // ui id (y9s3+?) + // there seems to be more to this, but its a quick fix for atk op swaps for now + var uiID uint64 + if r.Header.CodeVersion >= Y9S3 { + if err = r.Seek([]byte{0x38, 0xDF, 0xEE, 0x88}); err != nil { + return err + } + if err = r.Skip(13); err != nil { + return err + } + if uiID, err = r.Uint64(); err != nil { + return err + } + } // Older versions of siege did not include profile ids profileID := "" var unknownId uint64 @@ -121,6 +135,7 @@ func readPlayer(r *Reader) error { Operator: Operator(op), Spawn: spawn, DissectID: id, + uiID: uiID, } if p.Operator != Recruit && p.Operator.Role() == Defense { p.Spawn = r.Header.Site // We cannot detect the spawn here on defense @@ -131,6 +146,7 @@ func readPlayer(r *Reader) error { Str("profileID", profileID). Hex("DissectID", id). Uint64("ID", p.ID). + Uint64("uiID", p.uiID). Str("spawn", spawn).Send() found := false for i, existing := range r.Header.Players { @@ -143,6 +159,7 @@ func readPlayer(r *Reader) error { r.Header.Players[i].Operator = p.Operator r.Header.Players[i].Spawn = p.Spawn r.Header.Players[i].DissectID = p.DissectID + r.Header.Players[i].uiID = p.uiID found = true break } @@ -158,27 +175,55 @@ func readAtkOpSwap(r *Reader) error { if err != nil { return err } - if err = r.Skip(5); err != nil { + o := Operator(op) + // before Y9S3 caster view overhaul + if r.Header.CodeVersion < Y9S3 { + if err = r.Skip(5); err != nil { + return err + } + id, err := r.Bytes(4) + if err != nil { + return err + } + i := r.PlayerIndexByID(id) + log.Debug().Hex("id", id).Interface("op", op).Msg("atk_op_swap") + if i > -1 { + r.Header.Players[i].Operator = o + u := MatchUpdate{ + Type: OperatorSwap, + Username: r.Header.Players[i].Username, + Time: r.timeRaw, + TimeInSeconds: r.time, + Operator: o, + } + r.MatchFeedback = append(r.MatchFeedback, u) + log.Debug().Interface("match_update", u).Send() + } + return nil + } + // after Y9S3 caster view overhaul + if err = r.Skip(402); err != nil { return err } - id, err := r.Bytes(4) + // id shows up in player data and in op swaps afaik + id, err := r.Uint64() if err != nil { return err } - i := r.PlayerIndexByID(id) - o := Operator(op) - log.Debug().Hex("id", id).Interface("op", op).Msg("atk_op_swap") - if i > -1 { - r.Header.Players[i].Operator = o - u := MatchUpdate{ - Type: OperatorSwap, - Username: r.Header.Players[i].Username, - Time: r.timeRaw, - TimeInSeconds: r.time, - Operator: o, - } - r.MatchFeedback = append(r.MatchFeedback, u) - log.Debug().Interface("match_update", u).Send() + for i, p := range r.Header.Players { + if p.uiID == id { + r.Header.Players[i].Operator = o + u := MatchUpdate{ + Type: OperatorSwap, + Username: p.Username, + Time: r.timeRaw, + TimeInSeconds: r.time, + Operator: o, + } + r.MatchFeedback = append(r.MatchFeedback, u) + log.Debug().Interface("match_update", u).Send() + break + } } return nil } diff --git a/dissect/time.go b/dissect/time.go index 7235362..b182c48 100644 --- a/dissect/time.go +++ b/dissect/time.go @@ -45,14 +45,23 @@ func readY7Time(r *Reader) error { func (r *Reader) roundEnd() { log.Debug().Msg("round_end") + planter := -1 deaths := make(map[int]int) sizes := make(map[int]int) roles := make(map[int]TeamRole) + for _, p := range r.Header.Players { sizes[p.TeamIndex] += 1 roles[p.TeamIndex] = r.Header.Teams[p.TeamIndex].Role } + + if r.Header.CodeVersion >= Y9S4 { + team0Won := r.Header.Teams[0].StartingScore < r.Header.Teams[0].Score + r.Header.Teams[0].Won = team0Won + r.Header.Teams[1].Won = !team0Won + } + for _, u := range r.MatchFeedback { switch u.Type { case Kill: @@ -77,11 +86,19 @@ func (r *Reader) roundEnd() { return } } + if planter > -1 { r.Header.Teams[r.Header.Players[planter].TeamIndex].Won = true r.Header.Teams[r.Header.Players[planter].TeamIndex].WinCondition = DefusedBomb return } + + // skip for now until we have a more reliable way of determining the win condition + // Y9S4 at least tells us who won now in the header with StartingScore + if r.Header.CodeVersion >= Y9S4 { + return + } + if deaths[0] == sizes[0] { if planter > -1 && roles[0] == Attack { // ignore attackers killed post-plant return @@ -98,10 +115,12 @@ func (r *Reader) roundEnd() { r.Header.Teams[0].WinCondition = KilledOpponents return } + i := 0 if roles[1] == Defense { i = 1 } + r.Header.Teams[i].Won = true r.Header.Teams[i].WinCondition = Time } diff --git a/dissect/version.go b/dissect/version.go index 52320e1..a050ab0 100644 --- a/dissect/version.go +++ b/dissect/version.go @@ -11,4 +11,6 @@ const ( Y9S1 int = 8111697 Y9S1Update3 int = 8211379 Y9S2 int = 8303162 + Y9S3 int = 8506016 + Y9S4 int = 8673114 ) diff --git a/go.mod b/go.mod index d7f5b15..febb5f7 100644 --- a/go.mod +++ b/go.mod @@ -4,17 +4,17 @@ go 1.23 require ( github.com/go-test/deep v1.1.0 - github.com/klauspost/compress v1.17.9 + github.com/klauspost/compress v1.17.11 github.com/rs/zerolog v1.33.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 - github.com/xuri/excelize/v2 v2.8.1 - golang.org/x/net v0.28.0 - golang.org/x/tools v0.24.0 + github.com/xuri/excelize/v2 v2.9.0 + golang.org/x/net v0.32.0 + golang.org/x/tools v0.27.0 ) require ( - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -23,7 +23,7 @@ require ( github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/richardlehane/mscfb v1.0.4 // indirect - github.com/richardlehane/msoleps v1.0.3 // indirect + github.com/richardlehane/msoleps v1.0.4 // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect @@ -33,12 +33,12 @@ require ( github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect - golang.org/x/mod v0.20.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/crypto v0.30.0 // indirect + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fd2a513..22291c6 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -15,6 +17,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -41,6 +45,8 @@ github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7 github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM= github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= +github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -68,31 +74,60 @@ github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7 github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= github.com/xuri/excelize/v2 v2.8.1 h1:pZLMEwK8ep+CLIUWpWmvW8IWE/yxqG0I1xcN6cVMGuQ= github.com/xuri/excelize/v2 v2.8.1/go.mod h1:oli1E4C3Pa5RXg1TBXn4ENCXDV5JUMlBluUhG7c+CEE= +github.com/xuri/excelize/v2 v2.9.0 h1:1tgOaEq92IOEumR1/JfYS/eR0KHOCsRv/rYXXh6YJQE= +github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmjiLfBS5hdE= github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A= github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= +golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=