aboutsummaryrefslogtreecommitdiffstats
path: root/demo
diff options
context:
space:
mode:
authorGravatar BanceDev 2026-02-16 16:31:54 -0500
committerGravatar BanceDev 2026-02-16 16:31:54 -0500
commitca90ebdfa8789654766c5d7969baa7afacd9ebd2 (patch)
tree9693e0c7a5af6713f4c5e39372dcf22d05844ec3 /demo
initial commitHEADmain
Diffstat (limited to 'demo')
-rw-r--r--demo/dem/data.go54
-rw-r--r--demo/dem/parse.go57
-rw-r--r--demo/dem/parse_test.go55
-rw-r--r--demo/dem/testdata/demo1.dembin0 -> 184471 bytes
-rw-r--r--demo/dem/testdata/demo2.dembin0 -> 152705 bytes
-rw-r--r--demo/dem/testdata/demo3.dembin0 -> 197679 bytes
-rw-r--r--demo/mvd/cmd.go93
-rw-r--r--demo/mvd/damagedone.go63
-rw-r--r--demo/mvd/demoinfo.go35
-rw-r--r--demo/mvd/hidden.go71
-rw-r--r--demo/mvd/mutliple.go58
-rw-r--r--demo/mvd/parse.go133
-rw-r--r--demo/mvd/parse_test.go59
-rw-r--r--demo/mvd/read.go46
-rw-r--r--demo/mvd/set.go35
-rw-r--r--demo/mvd/testdata/demo1.mvdbin0 -> 5535264 bytes
-rw-r--r--demo/mvd/testdata/demo2.mvdbin0 -> 816709 bytes
-rw-r--r--demo/mvd/testdata/demo3.mvdbin0 -> 4541251 bytes
-rw-r--r--demo/mvd/testdata/demo4.mvdbin0 -> 428923 bytes
-rw-r--r--demo/mvd/unknown.go25
-rw-r--r--demo/mvd/usercommand.go100
-rw-r--r--demo/mvd/weapon.go71
-rw-r--r--demo/mvd/weaponinstruction.go57
-rw-r--r--demo/mvd/weaponserverside.go75
-rw-r--r--demo/qwd/cmd.go94
-rw-r--r--demo/qwd/data.go32
-rw-r--r--demo/qwd/parse.go66
-rw-r--r--demo/qwd/parse_test.go67
-rw-r--r--demo/qwd/read.go42
-rw-r--r--demo/qwd/set.go35
-rw-r--r--demo/qwd/testdata/demo1.qwdbin0 -> 13326081 bytes
-rw-r--r--demo/qwd/testdata/demo2.qwdbin0 -> 14619553 bytes
-rw-r--r--demo/qwd/testdata/demo3.qwdbin0 -> 6568269 bytes
-rw-r--r--demo/qwd/testdata/demo4.qwdbin0 -> 3599960 bytes
-rw-r--r--demo/qwd/testdata/demo5.qwdbin0 -> 9282497 bytes
-rw-r--r--demo/qwd/testdata/demo6.qwdbin0 -> 13509417 bytes
36 files changed, 1423 insertions, 0 deletions
diff --git a/demo/dem/data.go b/demo/dem/data.go
new file mode 100644
index 0000000..87ff90a
--- /dev/null
+++ b/demo/dem/data.go
@@ -0,0 +1,54 @@
+package dem
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+ "github.com/osm/quake/packet"
+ "github.com/osm/quake/packet/svc"
+)
+
+type Data struct {
+ Size uint32
+ Angle [3]float32
+ Packet packet.Packet
+}
+
+func (d *Data) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutUint32(d.Size)
+
+ for i := 0; i < 3; i++ {
+ buf.PutFloat32(d.Angle[i])
+ }
+
+ buf.PutBytes(d.Packet.Bytes())
+
+ return buf.Bytes()
+}
+
+func parseData(ctx *context.Context, buf *buffer.Buffer) (*Data, error) {
+ var err error
+ var data Data
+
+ if data.Size, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < 3; i++ {
+ if data.Angle[i], err = buf.GetFloat32(); err != nil {
+ return nil, err
+ }
+ }
+
+ bytes, err := buf.GetBytes(int(data.Size))
+ if err != nil {
+ return nil, err
+ }
+
+ if data.Packet, err = svc.Parse(ctx, bytes); err != nil {
+ return nil, err
+ }
+
+ return &data, nil
+}
diff --git a/demo/dem/parse.go b/demo/dem/parse.go
new file mode 100644
index 0000000..203a711
--- /dev/null
+++ b/demo/dem/parse.go
@@ -0,0 +1,57 @@
+package dem
+
+import (
+ "errors"
+
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+var ErrUnknownType = errors.New("unknown type")
+
+type Demo struct {
+ CDTrack []byte
+ Data []*Data
+}
+
+func (dem *Demo) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutBytes(dem.CDTrack)
+
+ for _, d := range dem.Data {
+ buf.PutBytes(d.Bytes())
+ }
+
+ return buf.Bytes()
+}
+
+func Parse(ctx *context.Context, data []byte) (*Demo, error) {
+ var demo Demo
+
+ buf := buffer.New(buffer.WithData(data))
+ ctx.SetIsDem(true)
+
+ for buf.Off() < buf.Len() {
+ b, err := buf.ReadByte()
+ if err != nil {
+ return nil, err
+ }
+
+ demo.CDTrack = append(demo.CDTrack, b)
+ if b == '\n' {
+ break
+ }
+ }
+
+ for buf.Off() < buf.Len() {
+ d, err := parseData(ctx, buf)
+ if err != nil {
+ return nil, err
+ }
+
+ demo.Data = append(demo.Data, d)
+ }
+
+ return &demo, nil
+}
diff --git a/demo/dem/parse_test.go b/demo/dem/parse_test.go
new file mode 100644
index 0000000..d3d33cf
--- /dev/null
+++ b/demo/dem/parse_test.go
@@ -0,0 +1,55 @@
+package dem
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "io/ioutil"
+ "testing"
+
+ "github.com/osm/quake/common/context"
+)
+
+type demTest struct {
+ filePath string
+ checksum string
+}
+
+var demTests = []demTest{
+ {
+ filePath: "testdata/demo1.dem",
+ checksum: "893e5279e3a84fed0416a1101dfc2b6bca1ebd28787323daa8e22cefe54883c1",
+ },
+ {
+ filePath: "testdata/demo2.dem",
+ checksum: "150e6fc54c24a70a44012ba71473a5cbbec081e19204a9eae98249621fef00d7",
+ },
+ {
+ filePath: "testdata/demo3.dem",
+ checksum: "7bee6edc47fe563cbf8f1e873d524d1e4cef7d919b523606e200c0d7c269bd83",
+ },
+}
+
+func TestParse(t *testing.T) {
+ for _, dt := range demTests {
+ t.Run(dt.filePath, func(t *testing.T) {
+ data, err := ioutil.ReadFile(dt.filePath)
+ if err != nil {
+ t.Errorf("unable to open demo file, %v", err)
+ }
+
+ demo, err := Parse(context.New(), data)
+ if err != nil {
+ t.Errorf("unable to parse demo, %v", err)
+ }
+
+ h := sha256.New()
+ h.Write(demo.Bytes())
+ checksum := fmt.Sprintf("%x", h.Sum(nil))
+ if checksum != dt.checksum {
+ t.Errorf("sha256 checksums didn't match")
+ t.Logf("output: %#v", checksum)
+ t.Logf("expected: %#v", dt.checksum)
+ }
+ })
+ }
+}
diff --git a/demo/dem/testdata/demo1.dem b/demo/dem/testdata/demo1.dem
new file mode 100644
index 0000000..2b821c3
--- /dev/null
+++ b/demo/dem/testdata/demo1.dem
Binary files differ
diff --git a/demo/dem/testdata/demo2.dem b/demo/dem/testdata/demo2.dem
new file mode 100644
index 0000000..9dd006e
--- /dev/null
+++ b/demo/dem/testdata/demo2.dem
Binary files differ
diff --git a/demo/dem/testdata/demo3.dem b/demo/dem/testdata/demo3.dem
new file mode 100644
index 0000000..d25c402
--- /dev/null
+++ b/demo/dem/testdata/demo3.dem
Binary files differ
diff --git a/demo/mvd/cmd.go b/demo/mvd/cmd.go
new file mode 100644
index 0000000..d89f492
--- /dev/null
+++ b/demo/mvd/cmd.go
@@ -0,0 +1,93 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type Cmd struct {
+ Msec byte
+ UserAngle [3]float32
+ Forward uint16
+ Side uint16
+ Up uint16
+ Buttons byte
+ Impulse byte
+ Padding [3]byte
+ Angle [3]float32
+}
+
+func (cmd *Cmd) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutByte(cmd.Msec)
+
+ for i := 0; i < 3; i++ {
+ buf.PutFloat32(cmd.UserAngle[i])
+ }
+
+ buf.PutUint16(cmd.Forward)
+ buf.PutUint16(cmd.Side)
+ buf.PutUint16(cmd.Up)
+ buf.PutByte(cmd.Buttons)
+ buf.PutByte(cmd.Impulse)
+
+ for i := 0; i < 3; i++ {
+ buf.PutByte(cmd.Padding[i])
+ }
+
+ for i := 0; i < 3; i++ {
+ buf.PutFloat32(cmd.Angle[i])
+ }
+
+ return buf.Bytes()
+}
+
+func parseCmd(ctx *context.Context, buf *buffer.Buffer) (*Cmd, error) {
+ var err error
+ var cmd Cmd
+
+ if cmd.Msec, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < 3; i++ {
+ if cmd.UserAngle[i], err = buf.GetFloat32(); err != nil {
+ return nil, err
+ }
+ }
+
+ if cmd.Forward, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Side, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Up, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Buttons, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Impulse, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < 3; i++ {
+ if cmd.Padding[i], err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+ }
+
+ for i := 0; i < 3; i++ {
+ if cmd.Angle[i], err = buf.GetFloat32(); err != nil {
+ return nil, err
+ }
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/mvd/damagedone.go b/demo/mvd/damagedone.go
new file mode 100644
index 0000000..cf9012a
--- /dev/null
+++ b/demo/mvd/damagedone.go
@@ -0,0 +1,63 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type DamageDone struct {
+ size uint32
+
+ Data []byte
+ DeathType uint16
+ AttackerEnt uint16
+ TargetEnt uint16
+ Damage uint16
+}
+
+func (cmd *DamageDone) Bytes() []byte {
+ if cmd.size != 8 {
+ return cmd.Data
+ }
+
+ buf := buffer.New()
+ buf.PutUint16(cmd.DeathType)
+ buf.PutUint16(cmd.AttackerEnt)
+ buf.PutUint16(cmd.TargetEnt)
+ buf.PutUint16(cmd.Damage)
+
+ return buf.Bytes()
+}
+
+func parseDamageDone(ctx *context.Context, buf *buffer.Buffer, size uint32) (*DamageDone, error) {
+ var err error
+ var cmd DamageDone
+
+ cmd.size = size
+ if cmd.size != 8 {
+ if cmd.Data, err = buf.GetBytes(int(cmd.size)); err != nil {
+ return nil, err
+ }
+
+ goto end
+ }
+
+ if cmd.DeathType, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.AttackerEnt, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.TargetEnt, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Damage, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+end:
+ return &cmd, nil
+}
diff --git a/demo/mvd/demoinfo.go b/demo/mvd/demoinfo.go
new file mode 100644
index 0000000..c52a645
--- /dev/null
+++ b/demo/mvd/demoinfo.go
@@ -0,0 +1,35 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type DemoInfo struct {
+ BlockNumber uint16
+ Data []byte
+}
+
+func (cmd *DemoInfo) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutUint16(cmd.BlockNumber)
+ buf.PutBytes(cmd.Data)
+
+ return buf.Bytes()
+}
+
+func parseDemoInfo(ctx *context.Context, buf *buffer.Buffer, size uint32) (*DemoInfo, error) {
+ var err error
+ var cmd DemoInfo
+
+ if cmd.BlockNumber, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Data, err = buf.GetBytes(int(size) - 2); err != nil {
+ return nil, err
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/mvd/hidden.go b/demo/mvd/hidden.go
new file mode 100644
index 0000000..f9b5ebf
--- /dev/null
+++ b/demo/mvd/hidden.go
@@ -0,0 +1,71 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+ "github.com/osm/quake/packet/command"
+ "github.com/osm/quake/protocol"
+ "github.com/osm/quake/protocol/mvd"
+)
+
+type HiddenCommand struct {
+ Size uint32
+ Type uint16
+ Command command.Command
+}
+
+func (cmd *HiddenCommand) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutUint32(cmd.Size)
+ buf.PutUint16(cmd.Type)
+ buf.PutBytes(cmd.Command.Bytes())
+
+ return buf.Bytes()
+}
+
+func parseHiddenCommands(ctx *context.Context, data []byte) ([]*HiddenCommand, error) {
+ var err error
+ var cmds []*HiddenCommand
+
+ buf := buffer.New(buffer.WithData(data))
+
+ for buf.Off() < buf.Len() {
+ var cmd HiddenCommand
+
+ if cmd.Size, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Type, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ var c command.Command
+ switch protocol.CommandType(cmd.Type) {
+ case mvd.HiddenUserCommand:
+ c, err = parseUserCommand(ctx, buf, cmd.Size)
+ case mvd.HiddenUserCommandWeapon:
+ c, err = parseWeapon(ctx, buf, cmd.Size)
+ case mvd.HiddenDemoInfo:
+ c, err = parseDemoInfo(ctx, buf, cmd.Size)
+ case mvd.HiddenDamangeDone:
+ c, err = parseDamageDone(ctx, buf, cmd.Size)
+ case mvd.HiddenUserCommandWeaponServerSide:
+ c, err = parseWeaponServerSide(ctx, buf, cmd.Size)
+ case mvd.HiddenUserCommandWeaponInstruction:
+ c, err = parseWeaponInstruction(ctx, buf, cmd.Size)
+ default:
+ c, err = parseUnknown(ctx, buf, cmd.Size)
+ }
+
+ if err != nil {
+ return cmds, err
+ }
+ cmd.Command = c
+
+ cmds = append(cmds, &cmd)
+ }
+
+ return cmds, nil
+}
diff --git a/demo/mvd/mutliple.go b/demo/mvd/mutliple.go
new file mode 100644
index 0000000..6b66d37
--- /dev/null
+++ b/demo/mvd/mutliple.go
@@ -0,0 +1,58 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+ "github.com/osm/quake/protocol/mvd"
+)
+
+type Multiple struct {
+ LastTo uint32
+ IsHiddenPacket bool
+ Size uint32
+ HiddenCommands []*HiddenCommand
+}
+
+func (cmd *Multiple) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutUint32(cmd.LastTo)
+
+ if cmd.IsHiddenPacket {
+ buf.PutUint32(cmd.Size)
+
+ for _, c := range cmd.HiddenCommands {
+ buf.PutBytes(c.Bytes())
+ }
+ }
+
+ return buf.Bytes()
+}
+
+func parseMultiple(ctx *context.Context, buf *buffer.Buffer) (*Multiple, error) {
+ var err error
+ var cmd Multiple
+
+ if cmd.LastTo, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ if cmd.LastTo == 0 && ctx.GetMVDProtocolExtension()&mvd.ExtensionHiddenMessages != 0 {
+ cmd.IsHiddenPacket = true
+
+ if cmd.Size, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ bytes, err := buf.GetBytes(int(cmd.Size))
+ if err != nil {
+ return nil, err
+ }
+
+ if cmd.HiddenCommands, err = parseHiddenCommands(ctx, bytes); err != nil {
+ return nil, err
+ }
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/mvd/parse.go b/demo/mvd/parse.go
new file mode 100644
index 0000000..20ea811
--- /dev/null
+++ b/demo/mvd/parse.go
@@ -0,0 +1,133 @@
+package mvd
+
+import (
+ "errors"
+
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+ "github.com/osm/quake/protocol"
+ "github.com/osm/quake/protocol/mvd"
+)
+
+var ErrUnknownType = errors.New("unknown type")
+
+type Demo struct {
+ Data []Data
+}
+
+type Data struct {
+ Target uint32
+ Timestamp byte
+ Command byte
+ Cmd *Cmd
+ Read *Read
+ Set *Set
+ Multiple *Multiple
+}
+
+func (d *Demo) Bytes() []byte {
+ buf := buffer.New()
+
+ for i := 0; i < len(d.Data); i++ {
+ buf.PutBytes(d.Data[i].Bytes())
+ }
+
+ return buf.Bytes()
+}
+
+func (d *Data) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutByte(d.Timestamp)
+ buf.PutByte(d.Command)
+
+ switch d.Command & 0x7 {
+ case mvd.DemoMultiple:
+ buf.PutBytes(d.Multiple.Bytes())
+ fallthrough
+ case mvd.DemoStats:
+ fallthrough
+ case mvd.DemoSingle:
+ fallthrough
+ case mvd.DemoAll:
+ fallthrough
+ case protocol.DemoRead:
+ buf.PutBytes(d.Read.Bytes())
+ case protocol.DemoSet:
+ buf.PutBytes(d.Set.Bytes())
+ case protocol.DemoCmd:
+ buf.PutBytes(d.Cmd.Bytes())
+ }
+
+ return buf.Bytes()
+}
+
+func Parse(ctx *context.Context, data []byte) (*Demo, error) {
+ var err error
+ var cmd Demo
+
+ buf := buffer.New(buffer.WithData(data))
+ ctx.SetIsMVD(true)
+
+ for buf.Off() < buf.Len() {
+ var data Data
+
+ process:
+ if data.Timestamp, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if data.Command, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ switch data.Command & 0x7 {
+ case mvd.DemoMultiple:
+ if data.Multiple, err = parseMultiple(ctx, buf); err != nil {
+ return nil, err
+ }
+ data.Target = data.Multiple.LastTo
+
+ if data.Multiple.IsHiddenPacket {
+ cmd.Data = append(cmd.Data, data)
+
+ if buf.Off() == buf.Len() {
+ goto end
+ }
+
+ goto process
+ }
+
+ fallthrough
+ case mvd.DemoStats:
+ fallthrough
+ case mvd.DemoSingle:
+ // Target determines which client the data is intended
+ // to reach and can be used in conjunction with the
+ // updateuserinfo to determine who the client is.
+ data.Target = uint32(data.Command >> 3)
+ fallthrough
+ case mvd.DemoAll:
+ fallthrough
+ case protocol.DemoRead:
+ if data.Read, err = parseRead(ctx, buf); err != nil {
+ return nil, err
+ }
+ case protocol.DemoSet:
+ if data.Set, err = parseSet(ctx, buf); err != nil {
+ return nil, err
+ }
+ case protocol.DemoCmd:
+ if data.Cmd, err = parseCmd(ctx, buf); err != nil {
+ return nil, err
+ }
+ default:
+ return nil, ErrUnknownType
+ }
+
+ cmd.Data = append(cmd.Data, data)
+ }
+
+end:
+ return &cmd, nil
+}
diff --git a/demo/mvd/parse_test.go b/demo/mvd/parse_test.go
new file mode 100644
index 0000000..53a00a3
--- /dev/null
+++ b/demo/mvd/parse_test.go
@@ -0,0 +1,59 @@
+package mvd
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "io/ioutil"
+ "testing"
+
+ "github.com/osm/quake/common/context"
+)
+
+type mvdTest struct {
+ filePath string
+ checksum string
+}
+
+var mvdTests = []mvdTest{
+ {
+ filePath: "testdata/demo1.mvd",
+ checksum: "9489430c9513aed5b8e444df1b0d2040acb55de565c1b3e237dce51c8c103c57",
+ },
+ {
+ filePath: "testdata/demo2.mvd",
+ checksum: "d40c3864dd3c052a8379e7e589cd045814ec462f6cf4432a2c94ec740843e30d",
+ },
+ {
+ filePath: "testdata/demo3.mvd",
+ checksum: "3dd728aa90fdae100ade62d9b93220b7689f6ebdca6c986d3c2b1208b4e1e33c",
+ },
+ {
+ filePath: "testdata/demo4.mvd",
+ checksum: "1f7b2c0ad77608f431028c8e68904400fe406150875a51f793f3395bf3784c90",
+ },
+}
+
+func TestParse(t *testing.T) {
+ for _, mt := range mvdTests {
+ t.Run(mt.filePath, func(t *testing.T) {
+ data, err := ioutil.ReadFile(mt.filePath)
+ if err != nil {
+ t.Errorf("unable to open demo file, %v", err)
+ }
+
+ demo, err := Parse(context.New(), data)
+ if err != nil {
+ t.Errorf("unable to parse demo, %v", err)
+ }
+
+ h := sha256.New()
+ h.Write(demo.Bytes())
+ checksum := fmt.Sprintf("%x", h.Sum(nil))
+ if checksum != mt.checksum {
+ t.Errorf("sha256 checksums didn't match")
+ t.Logf("output: %#v", checksum)
+ t.Logf("expected: %#v", mt.checksum)
+ }
+ })
+ }
+}
diff --git a/demo/mvd/read.go b/demo/mvd/read.go
new file mode 100644
index 0000000..20dae8a
--- /dev/null
+++ b/demo/mvd/read.go
@@ -0,0 +1,46 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+ "github.com/osm/quake/packet"
+ "github.com/osm/quake/packet/svc"
+)
+
+type Read struct {
+ Size uint32
+ Packet packet.Packet
+}
+
+func (cmd *Read) Bytes() []byte {
+ if cmd == nil || cmd.Packet == nil {
+ return []byte{}
+ }
+
+ buf := buffer.New()
+
+ buf.PutUint32(cmd.Size)
+ buf.PutBytes(cmd.Packet.Bytes())
+
+ return buf.Bytes()
+}
+
+func parseRead(ctx *context.Context, buf *buffer.Buffer) (*Read, error) {
+ var err error
+ var cmd Read
+
+ if cmd.Size, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ bytes, err := buf.GetBytes(int(cmd.Size))
+ if err != nil {
+ return nil, err
+ }
+
+ if cmd.Packet, err = svc.Parse(ctx, bytes); err != nil {
+ return nil, err
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/mvd/set.go b/demo/mvd/set.go
new file mode 100644
index 0000000..8cc4c95
--- /dev/null
+++ b/demo/mvd/set.go
@@ -0,0 +1,35 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type Set struct {
+ SeqOut uint32
+ SeqIn uint32
+}
+
+func (cmd *Set) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutUint32(cmd.SeqOut)
+ buf.PutUint32(cmd.SeqIn)
+
+ return buf.Bytes()
+}
+
+func parseSet(ctx *context.Context, buf *buffer.Buffer) (*Set, error) {
+ var err error
+ var cmd Set
+
+ if cmd.SeqOut, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ if cmd.SeqIn, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/mvd/testdata/demo1.mvd b/demo/mvd/testdata/demo1.mvd
new file mode 100644
index 0000000..052b266
--- /dev/null
+++ b/demo/mvd/testdata/demo1.mvd
Binary files differ
diff --git a/demo/mvd/testdata/demo2.mvd b/demo/mvd/testdata/demo2.mvd
new file mode 100644
index 0000000..c8f32e4
--- /dev/null
+++ b/demo/mvd/testdata/demo2.mvd
Binary files differ
diff --git a/demo/mvd/testdata/demo3.mvd b/demo/mvd/testdata/demo3.mvd
new file mode 100644
index 0000000..374af38
--- /dev/null
+++ b/demo/mvd/testdata/demo3.mvd
Binary files differ
diff --git a/demo/mvd/testdata/demo4.mvd b/demo/mvd/testdata/demo4.mvd
new file mode 100644
index 0000000..d2589ec
--- /dev/null
+++ b/demo/mvd/testdata/demo4.mvd
Binary files differ
diff --git a/demo/mvd/unknown.go b/demo/mvd/unknown.go
new file mode 100644
index 0000000..ec0747a
--- /dev/null
+++ b/demo/mvd/unknown.go
@@ -0,0 +1,25 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type Unknown struct {
+ Data []byte
+}
+
+func (cmd *Unknown) Bytes() []byte {
+ return cmd.Data
+}
+
+func parseUnknown(ctx *context.Context, buf *buffer.Buffer, size uint32) (*Unknown, error) {
+ var err error
+ var cmd Unknown
+
+ if cmd.Data, err = buf.GetBytes(int(size)); err != nil {
+ return nil, err
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/mvd/usercommand.go b/demo/mvd/usercommand.go
new file mode 100644
index 0000000..fc06b18
--- /dev/null
+++ b/demo/mvd/usercommand.go
@@ -0,0 +1,100 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type UserCommand struct {
+ size uint32
+
+ Data []byte
+ PlayerIndex byte
+ DropIndex byte
+ Msec byte
+ Angle [3]float32
+ Forward uint16
+ Side uint16
+ Up uint16
+ Buttons byte
+ Impulse byte
+}
+
+func (cmd *UserCommand) Bytes() []byte {
+ if cmd.size != 23 {
+ return cmd.Data
+ }
+
+ buf := buffer.New()
+
+ buf.PutByte(cmd.PlayerIndex)
+ buf.PutByte(cmd.DropIndex)
+ buf.PutByte(cmd.Msec)
+
+ for _, a := range cmd.Angle {
+ buf.PutFloat32(a)
+ }
+
+ buf.PutUint16(cmd.Forward)
+ buf.PutUint16(cmd.Side)
+ buf.PutUint16(cmd.Up)
+ buf.PutByte(cmd.Buttons)
+ buf.PutByte(cmd.Impulse)
+
+ return buf.Bytes()
+}
+
+func parseUserCommand(ctx *context.Context, buf *buffer.Buffer, size uint32) (*UserCommand, error) {
+ var err error
+ var cmd UserCommand
+
+ cmd.size = size
+ if cmd.size != 23 {
+ if cmd.Data, err = buf.GetBytes(int(cmd.size)); err != nil {
+ return nil, err
+ }
+
+ goto end
+ }
+
+ if cmd.PlayerIndex, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.DropIndex, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Msec, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < 3; i++ {
+ if cmd.Angle[i], err = buf.GetFloat32(); err != nil {
+ return nil, err
+ }
+ }
+
+ if cmd.Forward, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Side, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Up, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Buttons, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Impulse, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+end:
+ return &cmd, nil
+}
diff --git a/demo/mvd/weapon.go b/demo/mvd/weapon.go
new file mode 100644
index 0000000..d7b6bf0
--- /dev/null
+++ b/demo/mvd/weapon.go
@@ -0,0 +1,71 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type Weapon struct {
+ PlayerIndex byte
+ Items uint32
+ Shells byte
+ Nails byte
+ Rockets byte
+ Cells byte
+ Choice byte
+ String string
+}
+
+func (cmd *Weapon) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutByte(cmd.PlayerIndex)
+ buf.PutUint32(cmd.Items)
+ buf.PutByte(cmd.Shells)
+ buf.PutByte(cmd.Nails)
+ buf.PutByte(cmd.Rockets)
+ buf.PutByte(cmd.Cells)
+ buf.PutByte(cmd.Choice)
+ buf.PutString(cmd.String)
+
+ return buf.Bytes()
+}
+
+func parseWeapon(ctx *context.Context, buf *buffer.Buffer, size uint32) (*Weapon, error) {
+ var err error
+ var cmd Weapon
+
+ if cmd.PlayerIndex, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Items, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Shells, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Nails, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Rockets, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Cells, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Choice, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.String, err = buf.GetString(); err != nil {
+ return nil, err
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/mvd/weaponinstruction.go b/demo/mvd/weaponinstruction.go
new file mode 100644
index 0000000..d923d1b
--- /dev/null
+++ b/demo/mvd/weaponinstruction.go
@@ -0,0 +1,57 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type WeaponInstruction struct {
+ PlayerIndex byte
+ Bits byte
+ Seq uint32
+ Mode uint32
+ WeaponList []byte
+}
+
+func (cmd *WeaponInstruction) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutByte(cmd.PlayerIndex)
+ buf.PutByte(cmd.Bits)
+ buf.PutUint32(cmd.Seq)
+ buf.PutUint32(cmd.Mode)
+ buf.PutBytes(cmd.WeaponList)
+
+ return buf.Bytes()
+}
+
+func parseWeaponInstruction(
+ ctx *context.Context,
+ buf *buffer.Buffer,
+ size uint32,
+) (*WeaponInstruction, error) {
+ var err error
+ var cmd WeaponInstruction
+
+ if cmd.PlayerIndex, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Bits, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Seq, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Mode, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ if cmd.WeaponList, err = buf.GetBytes(10); err != nil {
+ return nil, err
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/mvd/weaponserverside.go b/demo/mvd/weaponserverside.go
new file mode 100644
index 0000000..36840e9
--- /dev/null
+++ b/demo/mvd/weaponserverside.go
@@ -0,0 +1,75 @@
+package mvd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type WeaponServerSide struct {
+ PlayerIndex byte
+ Items uint32
+ Shells byte
+ Nails byte
+ Rockets byte
+ Cells byte
+ Choice byte
+ String string
+}
+
+func (cmd *WeaponServerSide) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutByte(cmd.PlayerIndex)
+ buf.PutUint32(cmd.Items)
+ buf.PutByte(cmd.Shells)
+ buf.PutByte(cmd.Nails)
+ buf.PutByte(cmd.Rockets)
+ buf.PutByte(cmd.Cells)
+ buf.PutByte(cmd.Choice)
+ buf.PutString(cmd.String)
+
+ return buf.Bytes()
+}
+
+func parseWeaponServerSide(
+ ctx *context.Context,
+ buf *buffer.Buffer,
+ size uint32,
+) (*WeaponServerSide, error) {
+ var err error
+ var cmd WeaponServerSide
+
+ if cmd.PlayerIndex, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Items, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Shells, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Nails, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Rockets, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Cells, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Choice, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.String, err = buf.GetString(); err != nil {
+ return nil, err
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/qwd/cmd.go b/demo/qwd/cmd.go
new file mode 100644
index 0000000..8bd4d15
--- /dev/null
+++ b/demo/qwd/cmd.go
@@ -0,0 +1,94 @@
+package qwd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type Cmd struct {
+ Msec byte
+ UserAngle [3]float32
+ Forward uint16
+ Side uint16
+ Up uint16
+ Buttons byte
+ Impulse byte
+ Padding [3]byte
+ Angle [3]float32
+}
+
+func (cmd *Cmd) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutByte(cmd.Msec)
+
+ for i := 0; i < 3; i++ {
+ buf.PutFloat32(cmd.UserAngle[i])
+ }
+
+ buf.PutUint16(cmd.Forward)
+ buf.PutUint16(cmd.Side)
+ buf.PutUint16(cmd.Up)
+
+ for i := 0; i < 3; i++ {
+ buf.PutByte(cmd.Padding[i])
+ }
+
+ buf.PutByte(cmd.Buttons)
+ buf.PutByte(cmd.Impulse)
+
+ for i := 0; i < 3; i++ {
+ buf.PutFloat32(cmd.Angle[i])
+ }
+
+ return buf.Bytes()
+}
+
+func parseCmd(ctx *context.Context, buf *buffer.Buffer) (*Cmd, error) {
+ var err error
+ var cmd Cmd
+
+ if cmd.Msec, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < 3; i++ {
+ if cmd.UserAngle[i], err = buf.GetFloat32(); err != nil {
+ return nil, err
+ }
+ }
+
+ if cmd.Forward, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Side, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Up, err = buf.GetUint16(); err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < 3; i++ {
+ if cmd.Padding[i], err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+ }
+
+ if cmd.Buttons, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ if cmd.Impulse, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < 3; i++ {
+ if cmd.Angle[i], err = buf.GetFloat32(); err != nil {
+ return nil, err
+ }
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/qwd/data.go b/demo/qwd/data.go
new file mode 100644
index 0000000..cc00c23
--- /dev/null
+++ b/demo/qwd/data.go
@@ -0,0 +1,32 @@
+package qwd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/protocol"
+)
+
+type Data struct {
+ Timestamp float32
+ Command byte
+ Cmd *Cmd
+ Read *Read
+ Set *Set
+}
+
+func (d *Data) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutFloat32(d.Timestamp)
+ buf.PutByte(d.Command)
+
+ switch d.Command {
+ case protocol.DemoCmd:
+ buf.PutBytes(d.Cmd.Bytes())
+ case protocol.DemoRead:
+ buf.PutBytes(d.Read.Bytes())
+ case protocol.DemoSet:
+ buf.PutBytes(d.Set.Bytes())
+ }
+
+ return buf.Bytes()
+}
diff --git a/demo/qwd/parse.go b/demo/qwd/parse.go
new file mode 100644
index 0000000..458f714
--- /dev/null
+++ b/demo/qwd/parse.go
@@ -0,0 +1,66 @@
+package qwd
+
+import (
+ "errors"
+
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+ "github.com/osm/quake/protocol"
+)
+
+var ErrUnknownType = errors.New("unknown type")
+
+type Demo struct {
+ Data []*Data
+}
+
+func (dem *Demo) Bytes() []byte {
+ buf := buffer.New()
+
+ for _, d := range dem.Data {
+ buf.PutBytes(d.Bytes())
+ }
+
+ return buf.Bytes()
+}
+
+func Parse(ctx *context.Context, data []byte) (*Demo, error) {
+ var err error
+ var cmd Demo
+
+ buf := buffer.New(buffer.WithData(data))
+ ctx.SetIsQWD(true)
+
+ for buf.Off() < buf.Len() {
+ var data Data
+
+ if data.Timestamp, err = buf.GetFloat32(); err != nil {
+ return nil, err
+ }
+
+ if data.Command, err = buf.ReadByte(); err != nil {
+ return nil, err
+ }
+
+ switch data.Command {
+ case protocol.DemoCmd:
+ if data.Cmd, err = parseCmd(ctx, buf); err != nil {
+ return nil, err
+ }
+ case protocol.DemoRead:
+ if data.Read, err = parseRead(ctx, buf); err != nil {
+ return nil, err
+ }
+ case protocol.DemoSet:
+ if data.Set, err = parseSet(ctx, buf); err != nil {
+ return nil, err
+ }
+ default:
+ return nil, ErrUnknownType
+ }
+
+ cmd.Data = append(cmd.Data, &data)
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/qwd/parse_test.go b/demo/qwd/parse_test.go
new file mode 100644
index 0000000..97e39df
--- /dev/null
+++ b/demo/qwd/parse_test.go
@@ -0,0 +1,67 @@
+package qwd
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "io/ioutil"
+ "testing"
+
+ "github.com/osm/quake/common/context"
+)
+
+type qwdTest struct {
+ filePath string
+ checksum string
+}
+
+var qwdTests = []qwdTest{
+ {
+ filePath: "testdata/demo1.qwd",
+ checksum: "849cf01cb3fe5c7161625dafdf03178536048d093eda885ca9b37fc673c3c72a",
+ },
+ {
+ filePath: "testdata/demo2.qwd",
+ checksum: "c90f0a6c9ba79fc7f84d90b12d923a3ad2c0e54849d8df6fd913264f147931b2",
+ },
+ {
+ filePath: "testdata/demo3.qwd",
+ checksum: "b816f366489d22ed70b84f105a19ce63810fee7295e3b3612fdf7e09c26fc9e9",
+ },
+ {
+ filePath: "testdata/demo4.qwd",
+ checksum: "6b9088a13fc08609ca58e9887379e743beae0c0752939c001ac35d81a8f9c5af",
+ },
+ {
+ filePath: "testdata/demo5.qwd",
+ checksum: "698b1961ee8ac009fff062255cece5ab6729c50df9f6ece2d1cf7f1c52406c13",
+ },
+ {
+ filePath: "testdata/demo6.qwd",
+ checksum: "796357af178feb0c5fd5bff9cc6423914a36cc028f172b92c1336f1000e48d74",
+ },
+}
+
+func TestParse(t *testing.T) {
+ for _, qt := range qwdTests {
+ t.Run(qt.filePath, func(t *testing.T) {
+ data, err := ioutil.ReadFile(qt.filePath)
+ if err != nil {
+ t.Errorf("unable to open demo file, %v", err)
+ }
+
+ demo, err := Parse(context.New(), data)
+ if err != nil {
+ t.Errorf("unable to parse demo, %v", err)
+ }
+
+ h := sha256.New()
+ h.Write(demo.Bytes())
+ checksum := fmt.Sprintf("%x", h.Sum(nil))
+ if checksum != qt.checksum {
+ t.Errorf("sha256 checksums didn't match")
+ t.Logf("output: %#v", checksum)
+ t.Logf("expected: %#v", qt.checksum)
+ }
+ })
+ }
+}
diff --git a/demo/qwd/read.go b/demo/qwd/read.go
new file mode 100644
index 0000000..fdcee77
--- /dev/null
+++ b/demo/qwd/read.go
@@ -0,0 +1,42 @@
+package qwd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+ "github.com/osm/quake/packet"
+ "github.com/osm/quake/packet/svc"
+)
+
+type Read struct {
+ Size uint32
+ Packet packet.Packet
+}
+
+func (cmd *Read) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutUint32(cmd.Size)
+ buf.PutBytes(cmd.Packet.Bytes())
+
+ return buf.Bytes()
+}
+
+func parseRead(ctx *context.Context, buf *buffer.Buffer) (*Read, error) {
+ var err error
+ var cmd Read
+
+ if cmd.Size, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ bytes, err := buf.GetBytes(int(cmd.Size))
+ if err != nil {
+ return nil, err
+ }
+
+ if cmd.Packet, err = svc.Parse(ctx, bytes); err != nil {
+ return nil, err
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/qwd/set.go b/demo/qwd/set.go
new file mode 100644
index 0000000..d7cc48d
--- /dev/null
+++ b/demo/qwd/set.go
@@ -0,0 +1,35 @@
+package qwd
+
+import (
+ "github.com/osm/quake/common/buffer"
+ "github.com/osm/quake/common/context"
+)
+
+type Set struct {
+ SeqOut uint32
+ SeqIn uint32
+}
+
+func (cmd *Set) Bytes() []byte {
+ buf := buffer.New()
+
+ buf.PutUint32(cmd.SeqOut)
+ buf.PutUint32(cmd.SeqIn)
+
+ return buf.Bytes()
+}
+
+func parseSet(ctx *context.Context, buf *buffer.Buffer) (*Set, error) {
+ var err error
+ var cmd Set
+
+ if cmd.SeqOut, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ if cmd.SeqIn, err = buf.GetUint32(); err != nil {
+ return nil, err
+ }
+
+ return &cmd, nil
+}
diff --git a/demo/qwd/testdata/demo1.qwd b/demo/qwd/testdata/demo1.qwd
new file mode 100644
index 0000000..afc2c30
--- /dev/null
+++ b/demo/qwd/testdata/demo1.qwd
Binary files differ
diff --git a/demo/qwd/testdata/demo2.qwd b/demo/qwd/testdata/demo2.qwd
new file mode 100644
index 0000000..55f5306
--- /dev/null
+++ b/demo/qwd/testdata/demo2.qwd
Binary files differ
diff --git a/demo/qwd/testdata/demo3.qwd b/demo/qwd/testdata/demo3.qwd
new file mode 100644
index 0000000..e04d332
--- /dev/null
+++ b/demo/qwd/testdata/demo3.qwd
Binary files differ
diff --git a/demo/qwd/testdata/demo4.qwd b/demo/qwd/testdata/demo4.qwd
new file mode 100644
index 0000000..005a143
--- /dev/null
+++ b/demo/qwd/testdata/demo4.qwd
Binary files differ
diff --git a/demo/qwd/testdata/demo5.qwd b/demo/qwd/testdata/demo5.qwd
new file mode 100644
index 0000000..a78e779
--- /dev/null
+++ b/demo/qwd/testdata/demo5.qwd
Binary files differ
diff --git a/demo/qwd/testdata/demo6.qwd b/demo/qwd/testdata/demo6.qwd
new file mode 100644
index 0000000..78e6abc
--- /dev/null
+++ b/demo/qwd/testdata/demo6.qwd
Binary files differ