Skip to content
This repository has been archived by the owner on Aug 2, 2021. It is now read-only.

Commit

Permalink
Add placement reading
Browse files Browse the repository at this point in the history
Signed-off-by: Knut Ahlers <knut@ahlers.me>
  • Loading branch information
Luzifer committed Nov 6, 2019
1 parent 5aa424f commit a9607ba
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 6 deletions.
59 changes: 53 additions & 6 deletions datatypes.go
@@ -1,6 +1,12 @@
package sii

import "strings"
import (
"bytes"
"regexp"
"strings"

"github.com/pkg/errors"
)

// See https://modding.scssoft.com/wiki/Documentation/Engine/Units

Expand All @@ -10,12 +16,50 @@ import "strings"

// float2-4 => [2]float - [4]float

type Placement struct {
X, Y, Z float64
W, X2, Y2, Z2 float64
var placementRegexp = regexp.MustCompile(`^\(([0-9.]+|&[0-9a-f]+), ([0-9.]+|&[0-9a-f]+), ([0-9.]+|&[0-9a-f]+)\) \(([0-9.]+|&[0-9a-f]+); ([0-9.]+|&[0-9a-f]+), ([0-9.]+|&[0-9a-f]+), ([0-9.]+|&[0-9a-f]+)\)$`)

// Placement contains 7 floats: (x, y, z) (w; x, y, z)
type Placement [7]float32

func (p Placement) MarshalSII() ([]byte, error) {
var siiFloats = [][]byte{}

for _, f := range p {
b, err := float2sii(f)
if err != nil {
return nil, errors.Wrap(err, "Unable to encode float")
}
siiFloats = append(siiFloats, b)
}

var buf = new(bytes.Buffer)

buf.Write([]byte("("))
bytes.Join(siiFloats[0:3], []byte(", "))
buf.Write([]byte(") ("))
buf.Write(siiFloats[3])
buf.Write([]byte("; "))
bytes.Join(siiFloats[4:7], []byte(", "))
buf.Write([]byte(")"))

return buf.Bytes(), nil
}

// TODO: Implement marshalling for Placement
func (p *Placement) UnmarshalSII(in []byte) error {
if !placementRegexp.Match(in) {
return errors.New("Input data does not match expected format")
}

grps := placementRegexp.FindSubmatch(in)
var err error
for i := 0; i < 7; i++ {
if p[i], err = sii2float(grps[i+1]); err != nil {
return errors.Wrap(err, "Unable to decode float")
}
}

return nil
}

// fixed => native type int

Expand All @@ -38,8 +82,10 @@ type Ptr struct {
unit *Unit
}

func (p Ptr) CanResolve() bool { return strings.HasPrefix(p.Target, ".") }
func (p Ptr) CanResolve() bool { return strings.HasPrefix(p.Target, ".") }

func (p Ptr) MarshalSII() []byte { return []byte(p.Target) }

func (p Ptr) Resolve() Block {
if p.Target == "null" {
return nil
Expand All @@ -52,6 +98,7 @@ func (p Ptr) Resolve() Block {
}
return nil
}

func (p *Ptr) UnmarshalSII(in []byte) error {
p.Target = string(in)
return nil
Expand Down
51 changes: 51 additions & 0 deletions helpers.go
@@ -0,0 +1,51 @@
package sii

import (
"bytes"
"encoding/binary"
"encoding/hex"
"strconv"

"github.com/pkg/errors"
)

func float2sii(f float32) ([]byte, error) {
var (
buf = new(bytes.Buffer)
err error
)

err = binary.Write(buf, binary.BigEndian, f)
if err != nil {
return nil, errors.Wrap(err, "Unable to encode float")
}

dst := make([]byte, hex.EncodedLen(buf.Len()))
hex.Encode(dst, buf.Bytes())

return append([]byte("&"), dst...), nil
}

func sii2float(f []byte) (float32, error) {
if f[0] != '&' {
out, err := strconv.ParseFloat(string(f), 32)
return float32(out), err
}

// Strip leading '&'
f = f[1:]

var (
err error
out float32
)

dst := make([]byte, hex.DecodedLen(len(f)))
_, err = hex.Decode(dst, f)
if err != nil {
return 0, errors.Wrap(err, "Unable to read hex format")
}

err = binary.Read(bytes.NewReader(dst), binary.BigEndian, &out)
return out, errors.Wrap(err, "Unable to decode hex notation")
}
51 changes: 51 additions & 0 deletions helpers_test.go
@@ -0,0 +1,51 @@
package sii

import "testing"

func TestSii2FloatConversion(t *testing.T) {
var (
err error
f float32
)

for b, exp := range map[string]float32{
"0.00250711967": 0.00250711967,
"1.0": 1.0,
"&3b244e7d": 0.00250711967,
"&3f46da61": 0.7767697,
"&47135818": 37720.0938,
} {
f, err = sii2float([]byte(b))
if err != nil {
t.Errorf("Conversion of %q failed: %s", b, err)
continue
}

if f != exp {
t.Errorf("Conversion of %q has unxpected result: %f != %f", b, f, exp)
}
}
}

func TestFloat2SiiConversion(t *testing.T) {
var (
err error
f []byte
)

for b, exp := range map[float32]string{
0.00250711967: "&3b244e7d",
0.7767697: "&3f46da61",
37720.0938: "&47135818",
} {
f, err = float2sii(b)
if err != nil {
t.Errorf("Conversion of %f failed: %s", b, err)
continue
}

if string(f) != exp {
t.Errorf("Conversion of %f has unxpected result: %s != %s", b, f, exp)
}
}
}

0 comments on commit a9607ba

Please sign in to comment.