<commit_before>package demoinfocs

import (
	"bytes"
	"encoding/binary"
	"io"
	"strconv"
	"strings"

	bit "github.com/markus-wa/demoinfocs-golang/v2/internal/bitread"
	common "github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs/common"
	events "github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs/events"
	msg "github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs/msg"
)

const (
	stNameInstanceBaseline = "instancebaseline"
	stNameUserInfo         = "userinfo"
	stNameModelPreCache    = "modelprecache"
)

type playerInfo struct {
	version     int64
	xuid        uint64
	name        string
	userID      int
	guid        string
	friendsID   int
	friendsName string
	// Custom files stuff (CRC)
	customFiles0 int
	customFiles1 int
	customFiles2 int
	customFiles3 int
	// Amount of downloaded files from the server
	filesDownloaded byte
	// Bots
	isFakePlayer bool
	// HLTV Proxy
	isHltv bool
}

func (p *parser) parseStringTables() {
	p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)

	tables := int(p.bitReader.ReadSingleByte())
	for i := 0; i < tables; i++ {
		tableName := p.bitReader.ReadString()
		p.parseSingleStringTable(tableName)
	}

	p.processModelPreCacheUpdate()
	p.bitReader.EndChunk()
}

func (p *parser) parseSingleStringTable(name string) {
	nStrings := p.bitReader.ReadSignedInt(16)
	for i := 0; i < nStrings; i++ {
		stringName := p.bitReader.ReadString()

		const roysMaxStringLength = 100
		if len(stringName) >= roysMaxStringLength {
			panic("Someone said that Roy said I should panic")
		}

		if p.bitReader.ReadBit() {
			userDataSize := p.bitReader.ReadSignedInt(16)
			data := p.bitReader.ReadBytes(userDataSize)

			switch name {
			case stNameUserInfo:
				player := parsePlayerInfo(bytes.NewReader(data))

				playerIndex, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse playerIndex from string")
				}

				p.rawPlayers[int(playerIndex)] = player

			case stNameInstanceBaseline:
				classID, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse id from string")
				}

				p.stParser.SetInstanceBaseline(int(classID), data)

			case stNameModelPreCache:
				p.modelPreCache = append(p.modelPreCache, stringName)

			default: // Irrelevant table
			}
		}
	}

	// Client side stuff, dgaf
	if p.bitReader.ReadBit() {
		strings2 := p.bitReader.ReadSignedInt(16)
		for i := 0; i < strings2; i++ {
			p.bitReader.ReadString()

			if p.bitReader.ReadBit() {
				p.bitReader.Skip(p.bitReader.ReadSignedInt(16))
			}
		}
	}
}

func (p *parser) handleUpdateStringTable(tab *msg.CSVCMsg_UpdateStringTable) { //nolint:wsl
	// No need for recoverFromUnexpectedEOF here as we do that in processStringTable already

	cTab := p.stringTables[tab.TableId]
	switch cTab.Name {
	case stNameUserInfo:
		fallthrough
	case stNameModelPreCache:
		fallthrough
	case stNameInstanceBaseline:
		// Only handle updates for the above types
		// Create fake CreateStringTable and handle it like one of those
		cTab.NumEntries = tab.NumChangedEntries
		cTab.StringData = tab.StringData
		p.processStringTable(cTab)
	}
}

func (p *parser) handleCreateStringTable(tab *msg.CSVCMsg_CreateStringTable) { //nolint:wsl
	// No need for recoverFromUnexpectedEOF here as we do that in processStringTable already

	p.processStringTable(tab)

	p.stringTables = append(p.stringTables, tab)

	p.eventDispatcher.Dispatch(events.StringTableCreated{TableName: tab.Name})
}

//nolint:funlen
func (p *parser) processStringTable(tab *msg.CSVCMsg_CreateStringTable) {
	defer func() {
		p.setError(recoverFromUnexpectedEOF(recover()))
	}()

	if tab.Name == stNameModelPreCache {
		for i := len(p.modelPreCache); i < int(tab.MaxEntries); i++ {
			p.modelPreCache = append(p.modelPreCache, "")
		}
	}

	br := bit.NewSmallBitReader(bytes.NewReader(tab.StringData))

	if br.ReadBit() {
		panic("Can't decode")
	}

	nTmp := tab.MaxEntries
	nEntryBits := 0

	for nTmp != 0 {
		nTmp >>= 1
		nEntryBits++
	}

	if nEntryBits > 0 {
		nEntryBits--
	}

	hist := make([]string, 0)
	lastEntry := -1<commit_msg>glangci-lint: ignore processStringTable() gocognit<commit_after>package demoinfocs

import (
	"bytes"
	"encoding/binary"
	"io"
	"strconv"
	"strings"

	bit "github.com/markus-wa/demoinfocs-golang/v2/internal/bitread"
	common "github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs/common"
	events "github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs/events"
	msg "github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs/msg"
)

const (
	stNameInstanceBaseline = "instancebaseline"
	stNameUserInfo         = "userinfo"
	stNameModelPreCache    = "modelprecache"
)

type playerInfo struct {
	version     int64
	xuid        uint64
	name        string
	userID      int
	guid        string
	friendsID   int
	friendsName string
	// Custom files stuff (CRC)
	customFiles0 int
	customFiles1 int
	customFiles2 int
	customFiles3 int
	// Amount of downloaded files from the server
	filesDownloaded byte
	// Bots
	isFakePlayer bool
	// HLTV Proxy
	isHltv bool
}

func (p *parser) parseStringTables() {
	p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)

	tables := int(p.bitReader.ReadSingleByte())
	for i := 0; i < tables; i++ {
		tableName := p.bitReader.ReadString()
		p.parseSingleStringTable(tableName)
	}

	p.processModelPreCacheUpdate()
	p.bitReader.EndChunk()
}

func (p *parser) parseSingleStringTable(name string) {
	nStrings := p.bitReader.ReadSignedInt(16)
	for i := 0; i < nStrings; i++ {
		stringName := p.bitReader.ReadString()

		const roysMaxStringLength = 100
		if len(stringName) >= roysMaxStringLength {
			panic("Someone said that Roy said I should panic")
		}

		if p.bitReader.ReadBit() {
			userDataSize := p.bitReader.ReadSignedInt(16)
			data := p.bitReader.ReadBytes(userDataSize)

			switch name {
			case stNameUserInfo:
				player := parsePlayerInfo(bytes.NewReader(data))

				playerIndex, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse playerIndex from string")
				}

				p.rawPlayers[int(playerIndex)] = player

			case stNameInstanceBaseline:
				classID, err := strconv.ParseInt(stringName, 10, 64)
				if err != nil {
					panic("Couldn't parse id from string")
				}

				p.stParser.SetInstanceBaseline(int(classID), data)

			case stNameModelPreCache:
				p.modelPreCache = append(p.modelPreCache, stringName)

			default: // Irrelevant table
			}
		}
	}

	// Client side stuff, dgaf
	if p.bitReader.ReadBit() {
		strings2 := p.bitReader.ReadSignedInt(16)
		for i := 0; i < strings2; i++ {
			p.bitReader.ReadString()

			if p.bitReader.ReadBit() {
				p.bitReader.Skip(p.bitReader.ReadSignedInt(16))
			}
		}
	}
}

func (p *parser) handleUpdateStringTable(tab *msg.CSVCMsg_UpdateStringTable) { //nolint:wsl
	// No need for recoverFromUnexpectedEOF here as we do that in processStringTable already

	cTab := p.stringTables[tab.TableId]
	switch cTab.Name {
	case stNameUserInfo:
		fallthrough
	case stNameModelPreCache:
		fallthrough
	case stNameInstanceBaseline:
		// Only handle updates for the above types
		// Create fake CreateStringTable and handle it like one of those
		cTab.NumEntries = tab.NumChangedEntries
		cTab.StringData = tab.StringData
		p.processStringTable(cTab)
	}
}

func (p *parser) handleCreateStringTable(tab *msg.CSVCMsg_CreateStringTable) { //nolint:wsl
	// No need for recoverFromUnexpectedEOF here as we do that in processStringTable already

	p.processStringTable(tab)

	p.stringTables = append(p.stringTables, tab)

	p.eventDispatcher.Dispatch(events.StringTableCreated{TableName: tab.Name})
}

//nolint:funlen,gocognit
func (p *parser) processStringTable(tab *msg.CSVCMsg_CreateStringTable) {
	defer func() {
		p.setError(recoverFromUnexpectedEOF(recover()))
	}()

	if tab.Name == stNameModelPreCache {
		for i := len(p.modelPreCache); i < int(tab.MaxEntries); i++ {
			p.modelPreCache = append(p.modelPreCache, "")
		}
	}

	br := bit.NewSmallBitReader(bytes.NewReader(tab.StringData))

	if br.ReadBit() {
		panic("Can't decode")
	}

	nTmp := tab.MaxEntries
	nEntryBits := 0

	for nTmp != 0 {
		nTmp >>= 1
		nEntryBits++
	}

	if nEntryBits > 0 {
		nEntryBits--
	}

	hist := make([]string, 0)
	lastEntry := -1
{'fraction_non_alphanumeric': 0.11521580102414046, 'fraction_numerical': 0.010972933430870519, 'mean_word_length': 3.9208158368326336, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5371198', 'n_tokens_mistral': 3228, 'n_tokens_neox': 2857, 'n_words': 616}