Skip to content

Commit

Permalink
fix: incomplete stringtables parsing
Browse files Browse the repository at this point in the history
fix #539
fix #532
fix #525
  • Loading branch information
akiver committed May 16, 2024
1 parent 83e98ac commit a22e220
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 36 deletions.
7 changes: 4 additions & 3 deletions pkg/demoinfocs/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ type GrenadeEvent struct {
GrenadeType common.EquipmentType
Grenade *common.Equipment // Maybe nil for InfernoStart & InfernoExpired since we don't know the thrower (at least in old demos)
Position r3.Vector
Thrower *common.Player // May be nil if the demo is partially corrupt (player is 'unconnected', see #156 and #172).
Thrower *common.Player // May be nil with POV demos or if the demo is partially corrupt (player is 'unconnected', see #156 and #172).
GrenadeEntityID int
}

Expand Down Expand Up @@ -301,7 +301,7 @@ const (
// BombEvent contains the common attributes of bomb events. Dont register
// handlers on this tho, you want BombEventIf for that.
type BombEvent struct {
Player *common.Player
Player *common.Player // Can be nil with POV demos
Site Bombsite
}

Expand Down Expand Up @@ -414,7 +414,7 @@ const (

// PlayerHurt signals that a player has been damaged.
type PlayerHurt struct {
Player *common.Player // May be nil if the demo is partially corrupt (player is 'unconnected', see #156 and #172).
Player *common.Player // May be nil with POV demos or if the demo is partially corrupt (player is 'unconnected', see #156 and #172).
Attacker *common.Player // May be nil if the player is taking world damage (e.g. fall damage) or if the demo is partially corrupt (player is 'unconnected', see #156 and #172).
Health int
Armor int
Expand Down Expand Up @@ -583,6 +583,7 @@ const (

WarnTypeUnknownEquipmentIndex
WarnTypeMissingItemDefinitionIndex
WarnTypeStringTableParsingFailure // Should happen only with CS2 POV demos
)

// ParserWarn signals that a non-fatal problem occurred during parsing.
Expand Down
7 changes: 3 additions & 4 deletions pkg/demoinfocs/game_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,13 +492,12 @@ func (geh gameEventHandler) playerHurt(data map[string]*msg.CSVCMsg_GameEventKey
armorDamageTaken = 100
}

if player != nil && (!geh.parser.isSource2() || (player.PlayerPawnEntity() != nil)) {
// m_iHealth & m_ArmorValue check for CS2 POV demos
if health == 0 && (!geh.parser.isSource2() || player.PlayerPawnEntity().Property("m_iHealth") != nil) {
if player != nil {
if health == 0 {
healthDamageTaken = player.Health()
}

if armor == 0 && (!geh.parser.isSource2() || player.PlayerPawnEntity().Property("m_ArmorValue") != nil) {
if armor == 0 {
armorDamageTaken = player.Armor()
}
}
Expand Down
65 changes: 36 additions & 29 deletions pkg/demoinfocs/stringtables.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ const (
)

// Parse a string table data blob, returning a list of item updates.
func parseStringTable(
func (p *parser) parseStringTable(

Check failure on line 297 in pkg/demoinfocs/stringtables.go

View workflow job for this annotation

GitHub Actions / golangci-lint

[golangci-lint] pkg/demoinfocs/stringtables.go#L297

Function 'parseStringTable' has too many statements (41 > 40) (funlen)
Raw output
pkg/demoinfocs/stringtables.go:297: Function 'parseStringTable' has too many statements (41 > 40) (funlen)
func (p *parser) parseStringTable(
buf []byte,
numUpdates int32,
name string,
Expand All @@ -308,6 +308,16 @@ func parseStringTable(
return items
}

defer func() {
err := recover()
if err != nil {
p.eventDispatcher.Dispatch(events.ParserWarn{
Type: events.WarnTypeStringTableParsingFailure,
Message: "failed to parse stringtable properly",
})
}
}()

// Create a reader for the buffer
r := bit.NewSmallBitReader(bytes.NewReader(buf))

Expand Down Expand Up @@ -372,41 +382,38 @@ func parseStringTable(
if len(keys) > stringtableKeyHistorySize {
keys = keys[1:]
}
}

// Some entries have a value.
hasValue := r.ReadBit()
if hasValue {
bitSize := uint(0)
isCompressed := false
// Some entries have a value.
hasValue := r.ReadBit()
if hasValue {

Check failure on line 389 in pkg/demoinfocs/stringtables.go

View workflow job for this annotation

GitHub Actions / golangci-lint

[golangci-lint] pkg/demoinfocs/stringtables.go#L389

`if hasValue` has complex nested blocks (complexity: 10) (nestif)
Raw output
pkg/demoinfocs/stringtables.go:389:3: `if hasValue` has complex nested blocks (complexity: 10) (nestif)
		if hasValue {
		^
bitSize := uint(0)
isCompressed := false
if userDataFixed {

Check failure on line 392 in pkg/demoinfocs/stringtables.go

View workflow job for this annotation

GitHub Actions / golangci-lint

[golangci-lint] pkg/demoinfocs/stringtables.go#L392

only one cuddle assignment allowed before if statement (wsl)
Raw output
pkg/demoinfocs/stringtables.go:392:4: only one cuddle assignment allowed before if statement (wsl)
			if userDataFixed {
			^
bitSize = uint(userDataSize)
} else {
if (flags & 0x1) != 0 {
isCompressed = r.ReadBit()
}

if userDataFixed {
bitSize = uint(userDataSize)
if variantBitCount {
bitSize = r.ReadUBitInt() * 8
} else {
if (flags & 0x1) != 0 {
isCompressed = r.ReadBit()
}

if variantBitCount {
bitSize = r.ReadUBitInt() * 8
} else {
bitSize = r.ReadInt(17) * 8
}
bitSize = r.ReadInt(17) * 8
}
}
value = r.ReadBits(int(bitSize))

Check failure on line 405 in pkg/demoinfocs/stringtables.go

View workflow job for this annotation

GitHub Actions / golangci-lint

[golangci-lint] pkg/demoinfocs/stringtables.go#L405

assignments should only be cuddled with other assignments (wsl)
Raw output
pkg/demoinfocs/stringtables.go:405:4: assignments should only be cuddled with other assignments (wsl)
			value = r.ReadBits(int(bitSize))
			^

value = r.ReadBits(int(bitSize))

if isCompressed {
tmp, err := snappy.Decode(nil, value)
if err != nil {
panic(fmt.Sprintf("unable to decode snappy compressed stringtable item (%s, %d, %s): %s", name, index, key, err))
}

value = tmp
if isCompressed {
tmp, err := snappy.Decode(nil, value)
if err != nil {
panic(fmt.Sprintf("unable to decode snappy compressed stringtable item (%s, %d, %s): %s", name, index, key, err))
}
value = tmp

Check failure on line 412 in pkg/demoinfocs/stringtables.go

View workflow job for this annotation

GitHub Actions / golangci-lint

[golangci-lint] pkg/demoinfocs/stringtables.go#L412

assignments should only be cuddled with other assignments (wsl)
Raw output
pkg/demoinfocs/stringtables.go:412:5: assignments should only be cuddled with other assignments (wsl)
				value = tmp
				^
}

items = append(items, &stringTableItem{index, key, value})
}

items = append(items, &stringTableItem{index, key, value})
}

return items
Expand All @@ -415,7 +422,7 @@ func parseStringTable(
var instanceBaselineKeyRegex = regexp.MustCompile(`^\d+:\d+$`)

func (p *parser) processStringTableS2(tab createStringTable) {
items := parseStringTable(tab.StringData, tab.GetNumEntries(), tab.GetName(), tab.GetUserDataFixedSize(), tab.GetUserDataSize(), tab.GetFlags(), tab.GetUsingVarintBitcounts())
items := p.parseStringTable(tab.StringData, tab.GetNumEntries(), tab.GetName(), tab.GetUserDataFixedSize(), tab.GetUserDataSize(), tab.GetFlags(), tab.GetUsingVarintBitcounts())

for _, item := range items {
switch tab.GetName() {
Expand Down

0 comments on commit a22e220

Please sign in to comment.