Adding modding folder with a hacked build.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package interpreters
|
||||
|
||||
// Predicate is a function that specifies whether a certain description
|
||||
// is active within another.
|
||||
type Predicate func(inst *Instance) bool
|
||||
|
||||
// Always is a predicate that returns true for every call.
|
||||
func Always(inst *Instance) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Never is a predicate that returns false for every call.
|
||||
func Never(inst *Instance) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Description is the meta information about a data interpreter. A description
|
||||
// is an immutable object that provides new descriptions with any modification.
|
||||
type Description struct {
|
||||
fields map[string]*entry
|
||||
refinements map[string]*refinement
|
||||
lastField *entry
|
||||
}
|
||||
|
||||
var empty = newDescription()
|
||||
|
||||
// New returns an empty description.
|
||||
func New() *Description {
|
||||
return empty
|
||||
}
|
||||
|
||||
func newDescription() *Description {
|
||||
return &Description{
|
||||
fields: make(map[string]*entry),
|
||||
refinements: make(map[string]*refinement)}
|
||||
}
|
||||
|
||||
func (desc *Description) clone() *Description {
|
||||
cloned := newDescription()
|
||||
|
||||
for key, e := range desc.fields {
|
||||
cloned.fields[key] = e
|
||||
}
|
||||
for key, r := range desc.refinements {
|
||||
cloned.refinements[key] = r
|
||||
}
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
// For returns an instance of an interpreter with the given data slice
|
||||
// as backing buffer.
|
||||
func (desc *Description) For(data []byte) *Instance {
|
||||
return &Instance{desc: desc, data: data}
|
||||
}
|
||||
|
||||
// With extends the given description with a new field.
|
||||
// The returned description is a new, separated object from the originating one.
|
||||
func (desc *Description) With(key string, byteStart int, byteCount int) *Description {
|
||||
cloned := desc.clone()
|
||||
cloned.lastField = &entry{start: byteStart, count: byteCount}
|
||||
cloned.fields[key] = cloned.lastField
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
// As sets the value range for the previously added field.
|
||||
func (desc *Description) As(fieldRange FieldRange) *Description {
|
||||
if desc.lastField == nil {
|
||||
panic("No field active")
|
||||
}
|
||||
desc.lastField.via = fieldRange
|
||||
return desc
|
||||
}
|
||||
|
||||
// Refining adds another description within the given one. The provided predicate
|
||||
// determines whether the refined description is active.
|
||||
func (desc *Description) Refining(key string, byteStart int, byteCount int, refined *Description, predicate Predicate) *Description {
|
||||
cloned := desc.clone()
|
||||
cloned.refinements[key] = &refinement{
|
||||
entry: entry{start: byteStart, count: byteCount},
|
||||
desc: refined,
|
||||
predicate: predicate}
|
||||
|
||||
return cloned
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package interpreters_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/inkyblackness/hacked/ss1/content/interpreters"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDescriptionWithReturnsANewDescription(t *testing.T) {
|
||||
second := interpreters.New().With("test", 0, 32)
|
||||
|
||||
require.NotNil(t, second)
|
||||
assert.NotEqual(t, interpreters.New(), second)
|
||||
}
|
||||
|
||||
func TestDescriptionWithCopiesPreviousFields(t *testing.T) {
|
||||
first := interpreters.New().With("field1", 0, 32)
|
||||
second := first.With("field2", 32, 32)
|
||||
onlyField2 := interpreters.New().With("field2", 32, 32)
|
||||
|
||||
assert.NotEqual(t, onlyField2, second)
|
||||
}
|
||||
|
||||
func TestDescriptionWithLeavesOriginalAlone(t *testing.T) {
|
||||
first := interpreters.New().With("field1", 0, 32)
|
||||
onlyField1 := interpreters.New().With("field1", 0, 32)
|
||||
|
||||
first.With("field2", 32, 32)
|
||||
|
||||
assert.Equal(t, onlyField1, first)
|
||||
}
|
||||
|
||||
func TestDescriptionForCreatesNewInstance(t *testing.T) {
|
||||
data := make([]byte, 10)
|
||||
inst := interpreters.New().For(data)
|
||||
|
||||
assert.NotNil(t, inst)
|
||||
}
|
||||
|
||||
func TestDescriptionRefiningReturnsANewDescription(t *testing.T) {
|
||||
refined := interpreters.New().With("field1", 0, 16)
|
||||
second := interpreters.New().Refining("test", 0, 4, refined, interpreters.Always)
|
||||
|
||||
require.NotNil(t, second)
|
||||
assert.NotEqual(t, interpreters.New(), second)
|
||||
}
|
||||
|
||||
func TestDescriptionRefiningCopiesPreviousFields(t *testing.T) {
|
||||
first := interpreters.New().With("fieldA", 0, 8)
|
||||
refined := interpreters.New().With("field1", 0, 16)
|
||||
second := first.Refining("test", 0, 4, refined, interpreters.Always)
|
||||
secondMissing := interpreters.New().Refining("test", 0, 4, refined, interpreters.Always)
|
||||
|
||||
assert.NotEqual(t, secondMissing, second)
|
||||
}
|
||||
|
||||
func TestDescriptionRefiningCopiesPreviousRefinements(t *testing.T) {
|
||||
first := interpreters.New().Refining("sub1", 0, 1, interpreters.New(), interpreters.Always)
|
||||
second := first.Refining("sub2", 0, 1, interpreters.New(), interpreters.Always)
|
||||
|
||||
assert.NotContains(t, first.For(nil).ActiveRefinements(), "sub2")
|
||||
assert.Contains(t, second.For(nil).ActiveRefinements(), "sub1")
|
||||
}
|
||||
|
||||
func TestDescriptionAsPanicsIfNoFieldIsActive(t *testing.T) {
|
||||
assert.PanicsWithValue(t, "No field active", func() {
|
||||
interpreters.New().As(func(simpl *interpreters.Simplifier) bool { return false })
|
||||
})
|
||||
}
|
||||
|
||||
func TestDescriptionAsRegistersRangeFunctionForLastField(t *testing.T) {
|
||||
rangeFuncCalled := false
|
||||
rangeFunc := func(simpl *interpreters.Simplifier) bool {
|
||||
rangeFuncCalled = true
|
||||
return true
|
||||
}
|
||||
desc := interpreters.New().With("fieldA", 0, 2).As(rangeFunc)
|
||||
|
||||
inst := desc.For([]byte{0x00, 0x01})
|
||||
simplifier := interpreters.NewSimplifier(func(minValue, maxValue int64, formatter interpreters.RawValueFormatter) {})
|
||||
inst.Describe("fieldA", simplifier)
|
||||
|
||||
assert.True(t, rangeFuncCalled)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package interpreters
|
||||
|
||||
// FieldRange is a function specializing the range of a field to a simplifier.
|
||||
type FieldRange func(*Simplifier) bool
|
||||
|
||||
type entry struct {
|
||||
start int
|
||||
count int
|
||||
via FieldRange
|
||||
}
|
||||
|
||||
func (e *entry) describe(simplifier *Simplifier) {
|
||||
if (e.via == nil) || !e.via(simplifier) {
|
||||
simplifier.rawValue(e)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package interpreters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Instance is one instantiated interpreter for a data block,
|
||||
// based on a predetermined description.
|
||||
type Instance struct {
|
||||
desc *Description
|
||||
data []byte
|
||||
}
|
||||
|
||||
// Raw returns the raw array.
|
||||
func (inst *Instance) Raw() []byte {
|
||||
return inst.data
|
||||
}
|
||||
|
||||
// Undefined returns a byte array that specifies which bits were not
|
||||
// specified. The array has the same length as the underlying buffer and
|
||||
// has all bits set to 1 where no field or active refinement is present.
|
||||
func (inst *Instance) Undefined() []byte {
|
||||
mask := bytes.Repeat([]byte{0xFF}, len(inst.data))
|
||||
|
||||
for _, e := range inst.desc.fields {
|
||||
if inst.isValidRange(e) {
|
||||
copy(mask[e.start:e.start+e.count], bytes.Repeat([]byte{0x00}, e.count))
|
||||
}
|
||||
}
|
||||
for key, r := range inst.desc.refinements {
|
||||
if r.predicate(inst) {
|
||||
refined := inst.Refined(key)
|
||||
subMask := refined.Undefined()
|
||||
|
||||
for index, m := range subMask {
|
||||
mask[r.start+index] &= m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mask
|
||||
}
|
||||
|
||||
// Keys returns an array of all registered keys, sorted by start index.
|
||||
func (inst *Instance) Keys() []string {
|
||||
return sortKeys(inst.desc.fields)
|
||||
}
|
||||
|
||||
// ActiveRefinements returns an array of all keys where the corresponding refinement
|
||||
// is active (The corresponding predicate returns true).
|
||||
func (inst *Instance) ActiveRefinements() (keys []string) {
|
||||
entries := make(map[string]*entry)
|
||||
for key, r := range inst.desc.refinements {
|
||||
entries[key] = &r.entry
|
||||
}
|
||||
sortedKeys := sortKeys(entries)
|
||||
for _, key := range sortedKeys {
|
||||
if inst.desc.refinements[key].predicate(inst) {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value associated with the given key. Should there be no
|
||||
// value for the requested key, the function returns 0.
|
||||
func (inst *Instance) Get(key string) uint32 {
|
||||
e := inst.desc.fields[key]
|
||||
value := uint32(0)
|
||||
|
||||
if e != nil && inst.isValidRange(e) {
|
||||
for i := 0; i < e.count; i++ {
|
||||
value = (value << 8) | uint32(inst.data[e.start+e.count-1-i])
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// Describe returns the description of a value key.
|
||||
func (inst *Instance) Describe(key string, simplifier *Simplifier) {
|
||||
e := inst.desc.fields[key]
|
||||
|
||||
if e != nil && inst.isValidRange(e) {
|
||||
e.describe(simplifier)
|
||||
}
|
||||
}
|
||||
|
||||
// Set stores the provided value with the given key. Should there be no
|
||||
// registration for the key, the function does nothing.
|
||||
func (inst *Instance) Set(key string, value uint32) {
|
||||
e := inst.desc.fields[key]
|
||||
|
||||
if e != nil && inst.isValidRange(e) {
|
||||
for i := 0; i < e.count; i++ {
|
||||
inst.data[e.start+i] = byte(value >> uint32(i*8))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refined returns an instance that was nested according to the description.
|
||||
// This method returns an instance for registered refinements even if their predicate
|
||||
// would not specify them being active.
|
||||
// Should the refinement not exist, an instance without any fields will be returned.
|
||||
func (inst *Instance) Refined(key string) (refined *Instance) {
|
||||
r := inst.desc.refinements[key]
|
||||
|
||||
if r != nil && inst.isValidRange(&r.entry) {
|
||||
refined = &Instance{
|
||||
desc: r.desc,
|
||||
data: inst.data[r.start : r.start+r.count]}
|
||||
} else {
|
||||
refined = New().For(nil)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (inst *Instance) isValidRange(e *entry) bool {
|
||||
available := len(inst.data)
|
||||
|
||||
return (e.start + e.count) <= available
|
||||
}
|
||||
|
||||
func sortKeys(entries map[string]*entry) []string {
|
||||
keys := make([]string, 0, len(entries))
|
||||
for key := range entries {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Slice(keys, func(a, b int) bool {
|
||||
entryA := entries[keys[a]]
|
||||
entryB := entries[keys[b]]
|
||||
return entryA.start < entryB.start
|
||||
})
|
||||
return keys
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package interpreters_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/inkyblackness/hacked/ss1/content/interpreters"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type InstanceSuite struct {
|
||||
suite.Suite
|
||||
|
||||
data []byte
|
||||
inst *interpreters.Instance
|
||||
}
|
||||
|
||||
func TestInstanceSuite(t *testing.T) {
|
||||
suite.Run(t, new(InstanceSuite))
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) SetupTest() {
|
||||
sub1 := interpreters.New().
|
||||
With("subField0", 0, 1).
|
||||
With("subField1", 1, 2)
|
||||
|
||||
sub2 := interpreters.New().
|
||||
With("subFieldA", 0, 2).
|
||||
With("subFieldB", 2, 1)
|
||||
|
||||
desc := interpreters.New().
|
||||
With("field0", 0, 1).
|
||||
With("field1", 1, 1).
|
||||
With("field2", 2, 2).
|
||||
With("field3", 4, 4).
|
||||
With("misaligned", 9, 2).
|
||||
With("beyond", 256, 16).
|
||||
Refining("sub1", 3, 3, sub1, interpreters.Always).
|
||||
Refining("sub2", 6, 3, sub2, func(inst *interpreters.Instance) bool { return inst.Get("field0") == 0 }).
|
||||
Refining("sub3", 7, 1, interpreters.New(), interpreters.Always)
|
||||
|
||||
suite.data = []byte{0x01, 0x5A, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}
|
||||
suite.inst = desc.For(suite.data)
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestGetReturnsZeroForUnknownKey() {
|
||||
result := suite.inst.Get("unknown")
|
||||
|
||||
assert.Equal(suite.T(), uint32(0), result)
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestGetReturnsValueLittleEndian() {
|
||||
result := suite.inst.Get("field2")
|
||||
|
||||
assert.Equal(suite.T(), uint32(0x0403), result)
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestGetReturnsZeroForKeyBeyondSize() {
|
||||
result := suite.inst.Get("beyond")
|
||||
|
||||
assert.Equal(suite.T(), uint32(0), result)
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestSetIgnoresMisalignedFields() {
|
||||
suite.inst.Set("misaligned", 0xEEFF)
|
||||
|
||||
assert.Equal(suite.T(), []byte{0x09, 0x0A}, suite.data[8:10])
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestSetStoresValue() {
|
||||
suite.inst.Set("field3", 0xAABBCCDD)
|
||||
|
||||
assert.Equal(suite.T(), []byte{0xDD, 0xCC, 0xBB, 0xAA}, suite.data[4:8])
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestRefinedForUnknownKeyReturnsNullObject() {
|
||||
refined := suite.inst.Refined("unknown")
|
||||
|
||||
require.NotNil(suite.T(), refined)
|
||||
assert.Equal(suite.T(), uint32(0), refined.Get("something"))
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestRefinedReturnsInstanceForSubsection() {
|
||||
refined := suite.inst.Refined("sub1")
|
||||
|
||||
assert.Equal(suite.T(), uint32(0x0605), refined.Get("subField1"))
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestRefinedAllowsModificationOfOriginalData() {
|
||||
refined := suite.inst.Refined("sub1")
|
||||
refined.Set("subField0", 0xAB)
|
||||
|
||||
assert.Equal(suite.T(), byte(0xAB), suite.data[3])
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestRefinedReturnsInstanceEvenIfNotActive() {
|
||||
refined := suite.inst.Refined("sub2")
|
||||
|
||||
assert.Equal(suite.T(), uint32(0x0807), refined.Get("subFieldA"))
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestKeysReturnsListOfKeysSortedByStartIndex() {
|
||||
keys := suite.inst.Keys()
|
||||
|
||||
assert.Equal(suite.T(), []string{"field0", "field1", "field2", "field3", "misaligned", "beyond"}, keys)
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestActiveRefinementsReturnsListOfActiveKeysSortedByStartIndex() {
|
||||
keys := suite.inst.ActiveRefinements()
|
||||
|
||||
assert.Equal(suite.T(), []string{"sub1", "sub3"}, keys)
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestActiveRefinementsCanChange() {
|
||||
suite.inst.Set("field0", 0)
|
||||
keys := suite.inst.ActiveRefinements()
|
||||
|
||||
assert.Equal(suite.T(), []string{"sub1", "sub2", "sub3"}, keys)
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestRawReturnsData() {
|
||||
data := suite.inst.Refined("sub2").Raw()
|
||||
|
||||
assert.Equal(suite.T(), []byte{0x07, 0x08, 0x09}, data)
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestRawAllowsModificationOfOriginalData() {
|
||||
suite.inst.Refined("sub2").Raw()[1] = 0xEF
|
||||
|
||||
assert.Equal(suite.T(), byte(0xEF), suite.data[7])
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestUndefinedReturnsUndefinedBits() {
|
||||
data := suite.inst.Undefined()
|
||||
|
||||
assert.Equal(suite.T(), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF}, data)
|
||||
}
|
||||
|
||||
func (suite *InstanceSuite) TestUndefinedConsidersActiveRefinements() {
|
||||
suite.inst.Set("field0", 0)
|
||||
data := suite.inst.Undefined()
|
||||
|
||||
assert.Equal(suite.T(), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, data)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package interpreters
|
||||
|
||||
type refinement struct {
|
||||
entry
|
||||
|
||||
desc *Description
|
||||
predicate Predicate
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package interpreters
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// RawValueFormatter provides the format function for given value.
|
||||
// If nothing special is to be displayed, an empty string should be returned.
|
||||
// The handler is responsible for adding the number value representation.
|
||||
type RawValueFormatter func(value int) string
|
||||
|
||||
// RawValueHandler is for a simple value range.
|
||||
type RawValueHandler func(minValue, maxValue int64, formatter RawValueFormatter)
|
||||
|
||||
// EnumValueHandler is for enumerated (mapped) values.
|
||||
type EnumValueHandler func(values map[uint32]string)
|
||||
|
||||
// BitfieldHandler is for bitfields.
|
||||
type BitfieldHandler func(values map[uint32]string)
|
||||
|
||||
// ObjectIDHandler is for object identifier in a level.
|
||||
type ObjectIDHandler func()
|
||||
|
||||
// SpecialHandler is for rare occasions.
|
||||
type SpecialHandler func()
|
||||
|
||||
// RotationHandler is for circular values.
|
||||
type RotationHandler func(minValue, maxValue int64)
|
||||
|
||||
func basicToString(value int) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// RangedValue creates a field range for specific minimum and maximum values.
|
||||
func RangedValue(minValue, maxValue int64) FieldRange {
|
||||
return FormattedRangedValue(minValue, maxValue, basicToString)
|
||||
}
|
||||
|
||||
// FormattedRangedValue is similar to RangedValue, and adds a formatting function.
|
||||
func FormattedRangedValue(minValue, maxValue int64, formatter RawValueFormatter) FieldRange {
|
||||
return func(simpl *Simplifier) bool {
|
||||
return simpl.rangedValue(minValue, maxValue, formatter)
|
||||
}
|
||||
}
|
||||
|
||||
// EnumValue creates a field range describing enumerated values.
|
||||
func EnumValue(values map[uint32]string) FieldRange {
|
||||
return func(simpl *Simplifier) bool {
|
||||
return simpl.enumValue(values)
|
||||
}
|
||||
}
|
||||
|
||||
// Bitfield creates a field range describing bitfield values.
|
||||
func Bitfield(values map[uint32]string) FieldRange {
|
||||
return func(simpl *Simplifier) bool {
|
||||
return simpl.bitfield(values)
|
||||
}
|
||||
}
|
||||
|
||||
// ObjectID creates a field range describing object identifier.
|
||||
func ObjectID() FieldRange {
|
||||
return func(simpl *Simplifier) bool {
|
||||
return simpl.objectIndex()
|
||||
}
|
||||
}
|
||||
|
||||
// RotationValue creates a field range for a rotational property.
|
||||
func RotationValue(minValue, maxValue int64) FieldRange {
|
||||
return func(simpl *Simplifier) bool {
|
||||
return simpl.rotation(minValue, maxValue)
|
||||
}
|
||||
}
|
||||
|
||||
// SpecialValue creates a field range for special fields.
|
||||
// Currently known special values:
|
||||
// * BinaryCodedDecimal - for keypads storing their number as BCD
|
||||
// * LevelTexture - index value into level texture list
|
||||
// * MaterialOrLevelTexture - index value into level texture list, or material (bit 7 toggles)
|
||||
// * VariableKey - for actions
|
||||
// * VariableCondition - for action conditions
|
||||
// * ObjectTriple - for 0x00CCSSTT selection
|
||||
// * ObjectHeight - for level height value 0..255
|
||||
// * MoveTileHeight - for change tile height action
|
||||
// * TileHeight - tile heights
|
||||
// * TileType - tile types
|
||||
// * Unknown - It is unclear whether this field would have any effect, none identified so far
|
||||
// * Ignored - Although values have been found in this field, they don't appear to have any effect
|
||||
// * Mistake - It is assumed that these values should have been placed somewhere else. Typical example: Container content
|
||||
// * Internal - Fields that are handled mainly by the engine and shouldn't be modified.
|
||||
func SpecialValue(specialType string) FieldRange {
|
||||
return func(simpl *Simplifier) bool {
|
||||
return simpl.specialValue(specialType)
|
||||
}
|
||||
}
|
||||
|
||||
// Simplifier forwards descriptions in a way the requester can use.
|
||||
type Simplifier struct {
|
||||
rawValueHandler RawValueHandler
|
||||
enumValueHandler EnumValueHandler
|
||||
bitfieldHandler BitfieldHandler
|
||||
objectIDHandler ObjectIDHandler
|
||||
rotationHandler RotationHandler
|
||||
specialHandler map[string]SpecialHandler
|
||||
}
|
||||
|
||||
// NewSimplifier returns a new instance of a simplifier, with the minimal
|
||||
// handler set.
|
||||
func NewSimplifier(rawValueHandler RawValueHandler) *Simplifier {
|
||||
return &Simplifier{
|
||||
rawValueHandler: rawValueHandler,
|
||||
specialHandler: make(map[string]SpecialHandler)}
|
||||
}
|
||||
|
||||
func (simpl *Simplifier) rawValue(e *entry) {
|
||||
max := int64(math.Pow(2, float64(e.count*8)))
|
||||
if max == 256 {
|
||||
simpl.rawValueHandler(0, 255, basicToString)
|
||||
} else {
|
||||
half := max / 2
|
||||
simpl.rawValueHandler(-1, half-1, basicToString)
|
||||
}
|
||||
}
|
||||
|
||||
func (simpl *Simplifier) rangedValue(minValue, maxValue int64, formatter RawValueFormatter) bool {
|
||||
simpl.rawValueHandler(minValue, maxValue, formatter)
|
||||
return true
|
||||
}
|
||||
|
||||
// SetEnumValueHandler registers the handler for enumerations.
|
||||
func (simpl *Simplifier) SetEnumValueHandler(handler EnumValueHandler) {
|
||||
simpl.enumValueHandler = handler
|
||||
}
|
||||
|
||||
func (simpl *Simplifier) enumValue(values map[uint32]string) (result bool) {
|
||||
if simpl.enumValueHandler != nil {
|
||||
simpl.enumValueHandler(values)
|
||||
result = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetBitfieldHandler registers the handler for bitfields.
|
||||
func (simpl *Simplifier) SetBitfieldHandler(handler BitfieldHandler) {
|
||||
simpl.bitfieldHandler = handler
|
||||
}
|
||||
|
||||
func (simpl *Simplifier) bitfield(values map[uint32]string) (result bool) {
|
||||
if simpl.bitfieldHandler != nil {
|
||||
simpl.bitfieldHandler(values)
|
||||
result = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetObjectIDHandler registers the handler for object indices.
|
||||
func (simpl *Simplifier) SetObjectIDHandler(handler ObjectIDHandler) {
|
||||
simpl.objectIDHandler = handler
|
||||
}
|
||||
|
||||
func (simpl *Simplifier) objectIndex() (result bool) {
|
||||
if simpl.objectIDHandler != nil {
|
||||
simpl.objectIDHandler()
|
||||
result = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetSpecialHandler registers the handler for special values.
|
||||
func (simpl *Simplifier) SetSpecialHandler(specialType string, handler SpecialHandler) {
|
||||
simpl.specialHandler[specialType] = handler
|
||||
}
|
||||
|
||||
func (simpl *Simplifier) specialValue(specialType string) (result bool) {
|
||||
handler, existing := simpl.specialHandler[specialType]
|
||||
if existing && (handler != nil) {
|
||||
handler()
|
||||
result = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetRotationHandler registers the handler for rotation values.
|
||||
func (simpl *Simplifier) SetRotationHandler(handler RotationHandler) {
|
||||
simpl.rotationHandler = handler
|
||||
}
|
||||
|
||||
func (simpl *Simplifier) rotation(minValue, maxValue int64) (result bool) {
|
||||
if simpl.rotationHandler != nil {
|
||||
simpl.rotationHandler(minValue, maxValue)
|
||||
result = true
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package interpreters // nolint: testpackage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSimplifierRawValueCallsHandler(t *testing.T) {
|
||||
var calledMinValue int64
|
||||
var calledMaxValue int64
|
||||
rawHandler := func(minValue, maxValue int64, formatter RawValueFormatter) {
|
||||
calledMinValue, calledMaxValue = minValue, maxValue
|
||||
}
|
||||
simpl := NewSimplifier(rawHandler)
|
||||
|
||||
simpl.rawValue(&entry{count: 2})
|
||||
|
||||
assert.Equal(t, int64(-1), calledMinValue)
|
||||
assert.Equal(t, int64(32767), calledMaxValue)
|
||||
}
|
||||
|
||||
func TestSimplifierEnumValueReturnsFalseIfNoHandlerRegistered(t *testing.T) {
|
||||
simpl := NewSimplifier(func(minValue, maxValue int64, formatter RawValueFormatter) {})
|
||||
|
||||
result := simpl.enumValue(map[uint32]string{})
|
||||
|
||||
assert.False(t, result)
|
||||
}
|
||||
|
||||
func TestSimplifierEnumValueCallsRegisteredHandler(t *testing.T) {
|
||||
result := map[uint32]string{}
|
||||
simpl := NewSimplifier(func(minValue, maxValue int64, formatter RawValueFormatter) {})
|
||||
simpl.SetEnumValueHandler(func(values map[uint32]string) {
|
||||
result = values
|
||||
})
|
||||
|
||||
expected := map[uint32]string{1: "value"}
|
||||
simpl.enumValue(expected)
|
||||
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
Reference in New Issue
Block a user