Adding modding folder with a hacked build.

This commit is contained in:
ml
2026-09-26 08:53:08 +02:00
parent cdb960dc71
commit c91080aba7
755 changed files with 50424 additions and 0 deletions
@@ -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)
}