GoSBHPF/SBHPFv1/serializer.go
zervo b2cc3359a8 Add SerializeBuffered function
Add a SerializeBuffered function to provide full tree serialization support to non-seeking writers.
2025-02-14 16:55:10 +01:00

167 lines
4.8 KiB
Go

package sbhpfv1
import (
"encoding/binary"
"errors"
"io"
)
// Serialize serializes a root node object into a complete SBHPF tree.
// This includes the start headers necessary for proper deserialization of raw files.
// NOTE: Writer must support seeking. If this is not possible, use the SerializeBuffered wrapper.
func Serialize(w io.Writer, root *Node) error {
// Write file header (version + feature flag).
if err := binary.Write(w, binary.LittleEndian, uint8(FormatVersion)); err != nil {
return err
}
if err := binary.Write(w, binary.LittleEndian, uint8(ExtendedFeatureFlag)); err != nil {
return err
}
// Serialize root node.
return SerializeNode(w, root)
}
// SerializeNode serializes a node object into a SBHPF node.
// NOTE: Writer must support seeking. If this is not possible, use the SerializeNodeBuffered wrapper.
func SerializeNode(w io.Writer, node *Node) error {
// Reserve space for node size ("Node Size" node header).
sizePos := getWriterPosition(w) // Save current position.
if err := binary.Write(w, binary.LittleEndian, uint32(0)); err != nil { // uint32 to align with 4 byte constraint.
return err
}
// Write property count ("Property Count" node header).
propCount := uint16(len(node.Properties)) // uint16 to align with 2 byte constraint.
if err := binary.Write(w, binary.LittleEndian, propCount); err != nil {
return err
}
// Write child node count ("Child Count" node header).
childCount := uint16(len(node.Children))
if err := binary.Write(w, binary.LittleEndian, childCount); err != nil {
return err
}
// Write node name length ("Name Length" node header).
nameLen := uint8(len(node.Name)) // uint8 to align with 1 byte constraint.
if err := binary.Write(w, binary.LittleEndian, nameLen); err != nil {
return err
}
// Write node name if name length > 0 ("Node Name" node component).
if nameLen > 0 {
if _, err := w.Write([]byte(node.Name)); err != nil {
return err
}
}
// Write properties.
for _, prop := range node.Properties {
if err := SerializeProperty(w, prop); err != nil {
return err
}
}
// Write child nodes.
for _, child := range node.Children {
if err := SerializeNode(w, child); err != nil {
return err
}
}
// Seek back and update node size.
endPos := getWriterPosition(w)
nodeSize := uint32(endPos - sizePos)
return writeUint32AtPosition(w, sizePos, nodeSize)
}
// SerializeProperty serializes a property object into a SBHPF property.
func SerializeProperty(w io.Writer, prop Property) error {
// Write key length
keyLen := uint8(len(prop.Key)) // uint8 to align with 1 byte constraint
if err := binary.Write(w, binary.LittleEndian, keyLen); err != nil {
return err
}
// Write type
if err := binary.Write(w, binary.LittleEndian, prop.Type); err != nil {
return err
}
// Write key
if _, err := w.Write([]byte(prop.Key)); err != nil {
return err
}
// Write value
switch PropertyType(prop.Type) {
case TypeString:
strVal, ok := prop.Value.(string)
if !ok {
return errors.New("invalid type for string property")
}
strLen := uint16(len(strVal))
if err := binary.Write(w, binary.LittleEndian, strLen); err != nil {
return err
}
_, err := w.Write([]byte(strVal))
return err
case TypeBool:
val, ok := prop.Value.(bool)
if !ok {
return errors.New("invalid type for boolean property")
}
return binary.Write(w, binary.LittleEndian, val)
case TypeUint16:
val, ok := prop.Value.(uint16)
if !ok {
return errors.New("invalid type for uint16 property")
}
return binary.Write(w, binary.LittleEndian, val)
case TypeUint32:
val, ok := prop.Value.(uint32)
if !ok {
return errors.New("invalid type for float32 property")
}
return binary.Write(w, binary.LittleEndian, val)
case TypeUint8:
val, ok := prop.Value.(uint8)
if !ok {
return errors.New("invalid type for uint8 property")
}
return binary.Write(w, binary.LittleEndian, val)
case TypeInt16:
val, ok := prop.Value.(int16)
if !ok {
return errors.New("invalid type for int16 property")
}
return binary.Write(w, binary.LittleEndian, val)
case TypeInt32:
val, ok := prop.Value.(int32)
if !ok {
return errors.New("invalid type for int32 property")
}
return binary.Write(w, binary.LittleEndian, val)
case TypeInt8:
val, ok := prop.Value.(int8)
if !ok {
return errors.New("invalid type for int8 property")
}
return binary.Write(w, binary.LittleEndian, val)
case TypeFloat32:
val, ok := prop.Value.(float32)
if !ok {
return errors.New("invalid type for float32 property")
}
return binary.Write(w, binary.LittleEndian, val)
case TypeFloat64:
val, ok := prop.Value.(float64)
if !ok {
return errors.New("invalid type for float64 property")
}
return binary.Write(w, binary.LittleEndian, val)
}
return errors.New("unsupported property type")
}