2024-06-20 00:26:04 +00:00
|
|
|
package chunk7
|
2024-06-07 07:38:35 +00:00
|
|
|
|
2024-06-25 04:19:51 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/teeworlds-go/go-teeworlds-protocol/packer"
|
|
|
|
)
|
|
|
|
|
2024-06-07 07:38:35 +00:00
|
|
|
const (
|
2024-06-17 04:24:08 +00:00
|
|
|
chunkFlagVital = 1
|
2024-06-07 07:38:35 +00:00
|
|
|
chunkFlagResend = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
type ChunkFlags struct {
|
2024-06-17 04:24:08 +00:00
|
|
|
Vital bool
|
2024-06-07 07:38:35 +00:00
|
|
|
Resend bool
|
|
|
|
}
|
|
|
|
|
2024-06-19 04:13:57 +00:00
|
|
|
func (flags *ChunkFlags) ToInt() int {
|
|
|
|
v := 0
|
|
|
|
if flags.Resend {
|
|
|
|
v |= chunkFlagResend
|
|
|
|
}
|
|
|
|
if flags.Vital {
|
|
|
|
v |= chunkFlagVital
|
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2024-06-07 07:38:35 +00:00
|
|
|
type ChunkHeader struct {
|
|
|
|
Flags ChunkFlags
|
|
|
|
Size int
|
|
|
|
// sequence number
|
|
|
|
// will be acknowledged in the packet header ack
|
2024-06-17 04:24:08 +00:00
|
|
|
Seq int
|
2024-06-07 07:38:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Chunk struct {
|
|
|
|
Header ChunkHeader
|
2024-06-17 04:24:08 +00:00
|
|
|
Data []byte
|
2024-06-07 07:38:35 +00:00
|
|
|
}
|
|
|
|
|
2024-06-19 04:13:57 +00:00
|
|
|
func (header *ChunkHeader) Pack() []byte {
|
|
|
|
len := 2
|
|
|
|
if header.Flags.Vital {
|
|
|
|
len = 3
|
|
|
|
}
|
|
|
|
data := make([]byte, len)
|
|
|
|
data[0] = (byte(header.Flags.ToInt()&0x03) << 6) | ((byte(header.Size) >> 6) & 0x3f)
|
|
|
|
data[1] = (byte(header.Size) & 0x3f)
|
|
|
|
if header.Flags.Vital {
|
|
|
|
data[1] |= (byte(header.Seq) >> 2) & 0xc0
|
|
|
|
data[2] = byte(header.Seq) & 0xff
|
|
|
|
}
|
|
|
|
return data
|
|
|
|
}
|
|
|
|
|
2024-06-25 04:19:51 +00:00
|
|
|
func (header *ChunkHeader) Unpack(u *packer.Unpacker) error {
|
|
|
|
data, err := u.GetRaw(2)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
flagBits := (data[0] >> 6) & 0x03
|
|
|
|
header.Flags.Vital = (flagBits & chunkFlagVital) != 0
|
|
|
|
header.Flags.Resend = (flagBits & chunkFlagResend) != 0
|
|
|
|
header.Size = (int(data[0]&0x3F) << 6) | (int(data[1]) & 0x3F)
|
|
|
|
|
|
|
|
if header.Flags.Vital {
|
|
|
|
thirdByte, err := u.GetByte()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
header.Seq = int((data[1]&0xC0)<<2) | int(thirdByte)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: give this a better name
|
|
|
|
//
|
|
|
|
// but it would be nice to have two unpack methods for all structs
|
|
|
|
//
|
|
|
|
// Unpack(u *packer.Unpacker)
|
|
|
|
// UnpackTodoFindAGoodName(data []byte)
|
|
|
|
//
|
|
|
|
// See https://github.com/teeworlds-go/go-teeworlds-protocol/issues/5
|
|
|
|
func (header *ChunkHeader) UnpackRaw(data []byte) error {
|
|
|
|
if len(data) < 2 {
|
|
|
|
return fmt.Errorf("size=%d not enough data to read chunk header", len(data))
|
|
|
|
}
|
|
|
|
|
2024-06-07 07:38:35 +00:00
|
|
|
flagBits := (data[0] >> 6) & 0x03
|
|
|
|
header.Flags.Vital = (flagBits & chunkFlagVital) != 0
|
|
|
|
header.Flags.Resend = (flagBits & chunkFlagResend) != 0
|
2024-06-17 04:24:08 +00:00
|
|
|
header.Size = (int(data[0]&0x3F) << 6) | (int(data[1]) & 0x3F)
|
2024-06-07 07:38:35 +00:00
|
|
|
|
|
|
|
if header.Flags.Vital {
|
2024-06-25 04:19:51 +00:00
|
|
|
if len(data) < 3 {
|
|
|
|
return fmt.Errorf("size=%d not enough data to read vital chunk header", len(data))
|
|
|
|
}
|
2024-06-17 04:24:08 +00:00
|
|
|
header.Seq = int((data[1]&0xC0)<<2) | int(data[2])
|
2024-06-07 07:38:35 +00:00
|
|
|
}
|
2024-06-25 04:19:51 +00:00
|
|
|
return nil
|
2024-06-07 07:38:35 +00:00
|
|
|
}
|