From 5053478537eac61db5fa7926cdc44ec3b5f09053 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Sat, 30 Sep 2023 17:40:19 +0300 Subject: finish stringtablesentry parsing besides instancebaseline (#18) --- pkg/classes/dataTables.go | 24 +++--- pkg/classes/stringTables.go | 204 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 195 insertions(+), 33 deletions(-) (limited to 'pkg/classes') diff --git a/pkg/classes/dataTables.go b/pkg/classes/dataTables.go index 27ab9e4..7e710bf 100644 --- a/pkg/classes/dataTables.go +++ b/pkg/classes/dataTables.go @@ -17,17 +17,17 @@ type SendTable struct { NeedsDecoder bool NetTableName string NumOfProps int16 - Props []prop + Props []SendTableProp } type ServerClassInfo struct { - ClassId uint16 + DataTableID uint16 ClassName string DataTableName string } -type prop struct { - SendPropType sendPropType +type SendTableProp struct { + SendPropType SendPropType SendPropName string SendPropFlags uint32 Priority uint8 @@ -71,8 +71,8 @@ func ParseSendTable(reader *bitreader.Reader) SendTable { if propType >= int8(7) { return sendTable } - prop := prop{ - SendPropType: sendPropType(propType), + prop := SendTableProp{ + SendPropType: SendPropType(propType), SendPropName: reader.TryReadString(), SendPropFlags: uint32(reader.TryReadBits(19)), Priority: reader.TryReadUInt8(), @@ -104,11 +104,11 @@ func ParseSendTable(reader *bitreader.Reader) SendTable { func ParseServerClassInfo(reader *bitreader.Reader, count int, numOfClasses int) ServerClassInfo { serverClassInfo := ServerClassInfo{ - ClassId: reader.TryReadUInt16(), + DataTableID: reader.TryReadUInt16(), ClassName: reader.TryReadString(), DataTableName: reader.TryReadString(), } - writer.TempAppendLine("\t\t\t[%d] %s (%s)", serverClassInfo.ClassId, serverClassInfo.ClassName, serverClassInfo.DataTableName) + writer.TempAppendLine("\t\t\t[%d] %s (%s)", serverClassInfo.DataTableID, serverClassInfo.ClassName, serverClassInfo.DataTableName) return serverClassInfo } @@ -127,10 +127,10 @@ func checkBit(val uint32, bit int) bool { return (val & (uint32(1) << bit)) != 0 } -type sendPropType int +type SendPropType int const ( - ESendPropTypeInt sendPropType = iota + ESendPropTypeInt SendPropType = iota ESendPropTypeFloat ESendPropTypeVector3 ESendPropTypeVector2 @@ -161,7 +161,7 @@ const ( ESendPropFlagChangesOften string = "ChangesOften" ) -func (prop prop) GetFlags() []string { +func (prop SendTableProp) GetFlags() []string { flags := []string{} if checkBit(prop.SendPropFlags, 0) { flags = append(flags, ESendPropFlagUnsigned) @@ -223,7 +223,7 @@ func (prop prop) GetFlags() []string { return flags } -func (sendPropType sendPropType) String() string { +func (sendPropType SendPropType) String() string { switch sendPropType { case ESendPropTypeInt: return "Int" diff --git a/pkg/classes/stringTables.go b/pkg/classes/stringTables.go index 01939b2..0ac3a2e 100644 --- a/pkg/classes/stringTables.go +++ b/pkg/classes/stringTables.go @@ -1,6 +1,9 @@ package classes import ( + "fmt" + "strings" + "github.com/pektezol/bitreader" "github.com/pektezol/demoparser/pkg/writer" ) @@ -16,19 +19,14 @@ type StringTable struct { Classes []StringTableClass } -type StringTableEntry struct { - Name string - EntryData StringTableEntryData -} - -type StringTableEntryData struct { - // TODO: Parse StringTableEntry -} - type StringTableClass struct { Name string Data string } +type StringTableEntry struct { + Name string + EntryData any +} func (stringTables *StringTables) ParseStringTables(reader *bitreader.Reader) { stringTables.Size = reader.TryReadSInt32() @@ -51,7 +49,7 @@ func (stringTable *StringTable) ParseStream(reader *bitreader.Reader) { for i := 0; i < int(entryCount); i++ { var entry StringTableEntry - entry.Parse(reader) + entry.Parse(stringTable.Name, reader) stringTable.TableEntries[i] = entry } if entryCount != 0 { @@ -76,25 +74,189 @@ func (stringTable *StringTable) ParseStream(reader *bitreader.Reader) { } } -func (stringTableEntry *StringTableEntry) Parse(reader *bitreader.Reader) { +func (stringTableClass *StringTableClass) Parse(reader *bitreader.Reader) { + stringTableClass.Name = reader.TryReadString() + writer.TempAppendLine("\t\t\tName: %s", stringTableClass.Name) + if reader.TryReadBool() { + stringTableClass.Data = reader.TryReadStringLength(uint64(reader.TryReadUInt16())) + writer.TempAppendLine("\t\t\tData: %s", stringTableClass.Data) + } +} + +func (stringTableEntry *StringTableEntry) Parse(tableName string, reader *bitreader.Reader) { stringTableEntry.Name = reader.TryReadString() + writer.TempAppendLine("\t\t\tName: %s", stringTableEntry.Name) if reader.TryReadBool() { byteLen, err := reader.ReadBits(16) if err != nil { return } - dataBsr := reader.TryReadBytesToSlice(byteLen) - _ = bitreader.NewReaderFromBytes(dataBsr, true) // TODO: Parse StringTableEntry - // stringTableEntry.EntryData.ParseStream(entryReader) + stringTableEntryReader := bitreader.NewReaderFromBytes(reader.TryReadBytesToSlice(byteLen), true) + switch tableName { + case StringTableUserInfo: + stringTableEntry.ParseUserInfo(stringTableEntryReader) + case StringTableServerQueryInfo: + stringTableEntry.ParseServerQueryInfo(stringTableEntryReader) + case StringTableGameRulesCreation: + stringTableEntry.ParseGamesRulesCreation(stringTableEntryReader) + case StringTableInfoPanel: + stringTableEntry.ParseInfoPanel(stringTableEntryReader) + case StringTableLightStyles: + stringTableEntry.ParseLightStyles(stringTableEntryReader) + case StringTableModelPreCache: + stringTableEntry.ParsePrecacheData(stringTableEntryReader) + case StringTableGenericPreCache: + stringTableEntry.ParsePrecacheData(stringTableEntryReader) + case StringTableSoundPreCache: + stringTableEntry.ParsePrecacheData(stringTableEntryReader) + case StringTableDecalPreCache: + stringTableEntry.ParsePrecacheData(stringTableEntryReader) + default: + stringTableEntry.ParseUnknown(stringTableEntryReader) + } } } -func (stringTableClass *StringTableClass) Parse(reader *bitreader.Reader) { - stringTableClass.Name = reader.TryReadString() - writer.TempAppendLine("\t\t\tName: %s", stringTableClass.Name) - if reader.TryReadBool() { - dataLen := reader.TryReadBits(16) - stringTableClass.Data = reader.TryReadStringLength(dataLen) - writer.TempAppendLine("\t\t\tData: %s", stringTableClass.Data) +func (stringTableEntry *StringTableEntry) ParseUserInfo(reader *bitreader.Reader) { + const SignedGuidLen int32 = 32 + const MaxPlayerNameLength int32 = 32 + userInfo := struct { + SteamID uint64 + Name string + UserID int32 + GUID string + FriendsID uint32 + FriendsName string + FakePlayer bool + IsHltv bool + CustomFiles []uint32 + FilesDownloaded uint8 + }{ + SteamID: reader.TryReadUInt64(), + Name: reader.TryReadStringLength(uint64(MaxPlayerNameLength)), + UserID: reader.TryReadSInt32(), + GUID: reader.TryReadStringLength(uint64(SignedGuidLen) + 1), + } + reader.SkipBytes(3) + userInfo.FriendsID = reader.TryReadUInt32() + userInfo.FriendsName = reader.TryReadStringLength(uint64(MaxPlayerNameLength)) + userInfo.FakePlayer = reader.TryReadUInt8() != 0 + userInfo.IsHltv = reader.TryReadUInt8() != 0 + reader.SkipBytes(2) + userInfo.CustomFiles = []uint32{reader.TryReadUInt32(), reader.TryReadUInt32(), reader.TryReadUInt32(), reader.TryReadUInt32()} + userInfo.FilesDownloaded = reader.TryReadUInt8() + reader.SkipBytes(3) + stringTableEntry.EntryData = userInfo + writer.TempAppendLine("\t\t\t\tSteam Account ID: %d", uint32((userInfo.SteamID&0xFFFFFFFF00000000)|userInfo.SteamID)) + writer.TempAppendLine("\t\t\t\tSteam Account Instance: %d", uint32(userInfo.SteamID>>32)&0x000FFFFF) + writer.TempAppendLine("\t\t\t\tSteam Account Type: %d", uint32(userInfo.SteamID>>52)&0xF) + writer.TempAppendLine("\t\t\t\tSteam Account Universe: %d", uint32(userInfo.SteamID>>56)&0xFF) + writer.TempAppendLine("\t\t\t\tName: %s", userInfo.Name) + writer.TempAppendLine("\t\t\t\tUser ID: %d", userInfo.UserID) + writer.TempAppendLine("\t\t\t\tGUID: %s", userInfo.GUID) + writer.TempAppendLine("\t\t\t\tFriends ID: %d", userInfo.FriendsID) + writer.TempAppendLine("\t\t\t\tFriends Name: %s", userInfo.FriendsName) + writer.TempAppendLine("\t\t\t\tFake Player: %t", userInfo.FakePlayer) + writer.TempAppendLine("\t\t\t\tIs Htlv: %t", userInfo.IsHltv) + if userInfo.CustomFiles != nil { + writer.TempAppendLine("\t\t\t\tCustom File CRCs: [logo: 0x%d, sounds: 0x%d, models: 0x%d, txt: 0x%d]", userInfo.CustomFiles[0], userInfo.CustomFiles[1], userInfo.CustomFiles[2], userInfo.CustomFiles[3]) } + writer.TempAppendLine("\t\t\t\tFiles Downloaded: %d", userInfo.FilesDownloaded) } + +func (stringTableEntry *StringTableEntry) ParseServerQueryInfo(reader *bitreader.Reader) { + serverQueryInfo := struct{ Port uint32 }{ + Port: reader.TryReadUInt32(), + } + stringTableEntry.EntryData = serverQueryInfo + writer.TempAppendLine("\t\t\t\tPort: %d", serverQueryInfo.Port) +} + +func (stringTableEntry *StringTableEntry) ParseGamesRulesCreation(reader *bitreader.Reader) { + gamesRulesCreation := struct{ Message string }{ + Message: reader.TryReadString(), + } + stringTableEntry.EntryData = gamesRulesCreation + writer.TempAppendLine("\t\t\t\tMessage: %s", gamesRulesCreation.Message) +} + +func (stringTableEntry *StringTableEntry) ParseInfoPanel(reader *bitreader.Reader) { + infoPanel := struct{ Message string }{ + Message: reader.TryReadString(), + } + stringTableEntry.EntryData = infoPanel + writer.TempAppendLine("\t\t\t\tMessage: %s", infoPanel.Message) +} + +func (stringTableEntry *StringTableEntry) ParseLightStyles(reader *bitreader.Reader) { + lightStyles := struct{ Values []byte }{} + str := reader.TryReadString() + if len(str) != 0 { + for _, c := range str { + value := byte((c - 'a') * 22) + lightStyles.Values = append(lightStyles.Values, value) + } + } + stringTableEntry.EntryData = lightStyles + if lightStyles.Values == nil { + writer.TempAppendLine("\t\t\t\t0 Frames (256)") + } else { + writer.TempAppendLine("\t\t\t\t%d frames: %v", len(lightStyles.Values), lightStyles.Values) + } +} + +func (stringTableEntry *StringTableEntry) ParsePrecacheData(reader *bitreader.Reader) { + type PrecacheFlag uint16 + const ( + None PrecacheFlag = 0 + FatalIfMissing PrecacheFlag = 1 + Preload PrecacheFlag = 1 << 1 + ) + precacheData := struct{ Flags uint8 }{ + Flags: uint8(reader.TryReadBits(2)), + } + getFlags := func(flags PrecacheFlag) []string { + var flagStrings []string + if flags&FatalIfMissing != 0 { + flagStrings = append(flagStrings, "FatalIfMissing") + } + if flags&Preload != 0 { + flagStrings = append(flagStrings, "Preload") + } + return flagStrings + } + writer.TempAppendLine("\t\t\t\tFlags: %v", getFlags(PrecacheFlag(precacheData.Flags))) +} + +func (stringTableEntry *StringTableEntry) ParseUnknown(reader *bitreader.Reader) { + unknown := reader.TryReadBitsToSlice(reader.TryReadRemainingBits()) + binaryString := "" + for _, byteValue := range unknown { + binaryString += fmt.Sprintf("%08b ", byteValue) + } + writer.TempAppendLine("\t\t\t\tUnknown: (%s)", strings.TrimSpace(binaryString)) +} + +const ( + StringTableDownloadables string = "downloadables" + StringTableModelPreCache string = "modelprecache" + StringTableGenericPreCache string = "genericprecache" + StringTableSoundPreCache string = "soundprecache" + StringTableDecalPreCache string = "decalprecache" + StringTableInstanceBaseLine string = "instancebaseline" + StringTableLightStyles string = "lightstyles" + StringTableUserInfo string = "userinfo" + StringTableServerQueryInfo string = "server_query_info" + StringTableParticleEffectNames string = "ParticleEffectNames" + StringTableEffectDispatch string = "EffectDispatch" + StringTableVguiScreen string = "VguiScreen" + StringTableMaterials string = "Materials" + StringTableInfoPanel string = "InfoPanel" + StringTableScenes string = "Scenes" + StringTableMeleeWeapons string = "MeleeWeapons" + StringTableGameRulesCreation string = "GameRulesCreation" + StringTableBlackMarket string = "BlackMarketTable" + // custom? + StringTableDynamicModels string = "DynamicModels" + StringTableServerMapCycle string = "ServerMapCycle" +) -- cgit v1.2.3