1 package info
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "fmt"
7
8 "go.formulabun.club/functional/strings"
9 )
10
11 var GetServerInfoPacket = [...]byte{
12 0x58, 0x46, 0x23, 0x01,
13 0x00,
14 0x00,
15 0x0C,
16 0x00,
17
18 0x01,
19 0x1f, 0x02, 0x00, 0x00,
20 }
21
22 type PacketType uint8
23
24 const (
25 PacketTypeServerInfo PacketType = 0x0D
26 PacketTypePlayerInfo PacketType = 0x0E
27 )
28
29 type packetHeader struct {
30 Checksum uint32
31 Ack uint8
32 Ackret uint8
33 PacketType PacketType
34 _ [1]byte
35 }
36
37 type serverInfoPacketRaw struct {
38 Header packetHeader
39 _ uint8
40 PacketVersion uint8
41 Application [16]byte
42 Version uint8
43 SubVersion uint8
44 NumberOfPlayer uint8
45 MaxPlayers uint8
46 GameType uint8
47 ModifiedGame bool
48 CheatsEnabled bool
49 KartVars uint8
50 FileNeededNum uint8
51 Time uint32
52 LevelTime uint32
53 ServerName [32]byte
54 MapName [8]byte
55 MapTitle [33]byte
56 MapMD5 [16]uint8
57 ActNum uint8
58 IsZone uint8
59 HttpSource [256]byte
60 FileNeeded [915]uint8
61 }
62
63 type ServerInfoPacket struct {
64 header packetHeader
65 PacketVersion uint8
66 Application [16]byte
67 Version uint8
68 SubVersion uint8
69 NumberOfPlayer uint8
70 MaxPlayers uint8
71 GameType uint8
72 ModifiedGame bool
73 CheatsEnabled bool
74 KartVars uint8
75 FileNeededNum uint8
76 Time uint32
77 LevelTime uint32
78 ServerNameRaw []byte
79 ServerName string
80 MapName string
81 MapTitle string
82 MapMD5 [16]uint8
83 ActNum uint8
84 IsZone uint8
85 HttpSource string
86 FileNeeded []FileNeededEntry
87 }
88
89 func (p ServerInfoPacket) GetPacketType() PacketType {
90 return p.header.PacketType
91 }
92
93 type playerInfoPacketRaw struct {
94 Header packetHeader
95 PlayerInfo [32]playerInfoEntryRaw
96 }
97
98 type playerInfoEntryRaw struct {
99 Node uint8
100 Name [21 + 1]byte
101 Address [4]uint8
102 Team uint8
103 Skin uint8
104 Data uint8
105 Score uint32
106 TimeInServer uint16
107 }
108
109 type PlayerInfoPacket struct {
110 header packetHeader
111 PlayerInfo []PlayerInfoEntry
112 }
113
114 func (p PlayerInfoPacket) GetPacketType() PacketType {
115 return p.header.PacketType
116 }
117
118 type PlayerInfoEntry struct {
119 Node uint8
120 Name string
121 Address [4]uint8
122 Team uint8
123 Skin uint8
124 Data uint8
125 Score uint32
126 TimeInServer uint16
127 }
128
129 type packet interface {
130 GetPacketType() PacketType
131 }
132
133 type FileNeededEntry struct {
134 WillSend bool
135 TotalSize uint32
136 FileName string
137 MD5 [16]uint8
138 }
139
140 func ParsePacket(data []byte) (packet, error) {
141 header := packetHeader{}
142 if err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &header); err != nil {
143 return nil, err
144 }
145
146 switch header.PacketType {
147 case PacketTypeServerInfo:
148 return parseServerInfoPacket(data)
149 case PacketTypePlayerInfo:
150 return parsePlayerInfoPacket(data)
151 default:
152 return nil, fmt.Errorf("unknown packet type: %d", header.PacketType)
153 }
154 }
155
156 func parseFileNeeded(data []byte, fileNeededCount int) []FileNeededEntry {
157 var entries []FileNeededEntry
158 buf := bytes.NewBuffer(data[:])
159 for i := 0; i < fileNeededCount; i++ {
160 entry := FileNeededEntry{}
161 binary.Read(buf, binary.LittleEndian, &entry.WillSend)
162 b, _ := buf.ReadByte()
163 entry.WillSend = (b >> 4) == 1
164 binary.Read(buf, binary.LittleEndian, &entry.TotalSize)
165 entry.FileName, _ = buf.ReadString(0)
166
167 entry.FileName = entry.FileName[:len(entry.FileName)-1]
168 binary.Read(buf, binary.LittleEndian, &entry.MD5)
169 entries = append(entries, entry)
170 }
171 return entries
172 }
173
174 func parseServerInfoPacket(data []byte) (ServerInfoPacket, error) {
175 var packetRaw serverInfoPacketRaw
176 if err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &packetRaw); err != nil {
177 return ServerInfoPacket{}, fmt.Errorf("failed to unpack server info packet: %v", err)
178 }
179 packet := ServerInfoPacket{
180 header: packetRaw.Header,
181 PacketVersion: packetRaw.PacketVersion,
182 Application: packetRaw.Application,
183 Version: packetRaw.Version,
184 SubVersion: packetRaw.SubVersion,
185 NumberOfPlayer: packetRaw.NumberOfPlayer,
186 MaxPlayers: packetRaw.MaxPlayers,
187 GameType: packetRaw.GameType,
188 ModifiedGame: packetRaw.ModifiedGame,
189 CheatsEnabled: packetRaw.CheatsEnabled,
190 KartVars: packetRaw.KartVars,
191 FileNeededNum: packetRaw.FileNeededNum,
192 Time: packetRaw.Time,
193 LevelTime: packetRaw.LevelTime,
194 ServerNameRaw: strings.NullTerminated(packetRaw.ServerName[:]),
195 ServerName: strings.SafeNullTerminated(packetRaw.ServerName[:]),
196 MapName: strings.SafeNullTerminated(packetRaw.MapName[:]),
197 MapTitle: strings.SafeNullTerminated(packetRaw.MapTitle[:]),
198 MapMD5: packetRaw.MapMD5,
199 ActNum: packetRaw.ActNum,
200 IsZone: packetRaw.IsZone,
201 HttpSource: strings.SafeNullTerminated(packetRaw.HttpSource[:]),
202 FileNeeded: parseFileNeeded(packetRaw.FileNeeded[:], int(packetRaw.FileNeededNum)),
203 }
204 return packet, nil
205 }
206
207 func parsePlayerInfoPacket(data []byte) (PlayerInfoPacket, error) {
208 var packetRaw playerInfoPacketRaw
209 if err := binary.Read(bytes.NewReader(data), binary.LittleEndian, &packetRaw); err != nil {
210 return PlayerInfoPacket{}, fmt.Errorf("failed to unpack playerinfo packet: %w", err)
211 }
212 packet := PlayerInfoPacket{
213 header: packetRaw.Header,
214 }
215 for i := 0; i < len(packetRaw.PlayerInfo); i++ {
216
217 if packetRaw.PlayerInfo[i].Node == 255 {
218 continue
219 }
220 entry := packetRaw.PlayerInfo[i]
221 packet.PlayerInfo = append(packet.PlayerInfo, PlayerInfoEntry{
222 Node: entry.Node,
223 Name: strings.SafeNullTerminated(entry.Name[:]),
224 Address: [4]uint8{
225 entry.Address[0],
226 entry.Address[1],
227 entry.Address[2],
228 entry.Address[3],
229 },
230 Team: entry.Team,
231 Skin: entry.Skin,
232 Data: entry.Data,
233 Score: entry.Score,
234 TimeInServer: entry.TimeInServer,
235 })
236 }
237 return packet, nil
238 }
239
View as plain text